Merge pull request #13686 from protocolbuffers/merge-upb

Merge upb into the protobuf repo
pull/13688/head
Adam Cozzette 1 year ago committed by GitHub
commit 81242c57c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      upb/.bazelci/presubmit.yml
  2. 1
      upb/.bazelignore
  3. 63
      upb/.bazelrc
  4. 3
      upb/.clang-format
  5. 21
      upb/.github/actions/setup-bazel-cache/action.yml
  6. 107
      upb/.github/workflows/bazel_tests.yml
  7. 22
      upb/.github/workflows/clang_format.yml
  8. 24
      upb/.github/workflows/generate_files.yml
  9. 15
      upb/.github/workflows/mergeable.yml
  10. 173
      upb/.github/workflows/python_tests.yml
  11. 6
      upb/.gitignore
  12. 744
      upb/BUILD
  13. 37
      upb/CONTRIBUTING.md
  14. 26
      upb/LICENSE
  15. 84
      upb/README.md
  16. 86
      upb/WORKSPACE
  17. 63
      upb/bazel/BUILD
  18. 134
      upb/bazel/amalgamate.py
  19. 85
      upb/bazel/amalgamation.bzl
  20. 101
      upb/bazel/build_defs.bzl
  21. 127
      upb/bazel/lua.BUILD
  22. 40
      upb/bazel/protobuf.patch
  23. 157
      upb/bazel/py_proto_library.bzl
  24. 84
      upb/bazel/python_downloads.bzl
  25. 293
      upb/bazel/system_python.bzl
  26. 530
      upb/bazel/upb_proto_library.bzl
  27. 104
      upb/bazel/workspace_deps.bzl
  28. 262
      upb/benchmarks/BUILD
  29. 59
      upb/benchmarks/BUILD.googleapis
  30. 390
      upb/benchmarks/benchmark.cc
  31. 88
      upb/benchmarks/build_defs.bzl
  32. 123
      upb/benchmarks/compare.py
  33. 888
      upb/benchmarks/descriptor.proto
  34. 890
      upb/benchmarks/descriptor_sv.proto
  35. 35
      upb/benchmarks/empty.proto
  36. 69
      upb/benchmarks/gen_protobuf_binary_cc.py
  37. 123
      upb/benchmarks/gen_synthetic_protos.py
  38. 70
      upb/benchmarks/gen_upb_binary_c.py
  39. 125
      upb/cmake/BUILD.bazel
  40. 129
      upb/cmake/CMakeLists.txt
  41. 23
      upb/cmake/README.md
  42. 77
      upb/cmake/build_defs.bzl
  43. 1327
      upb/cmake/google/protobuf/descriptor.upb.c
  44. 6236
      upb/cmake/google/protobuf/descriptor.upb.h
  45. 420
      upb/cmake/make_cmakelists.py
  46. 73
      upb/cmake/push_auto_update.sh
  47. 60
      upb/cmake/staleness_test.py
  48. 194
      upb/cmake/staleness_test_lib.py
  49. 429
      upb/docs/design.md
  50. 73
      upb/docs/render.py
  51. 65
      upb/docs/style-guide.md
  52. 261
      upb/docs/vs-cpp-protos.md
  53. 444
      upb/docs/wrapping-upb.md
  54. 130
      upb/lua/BUILD.bazel
  55. 8
      upb/lua/README.md
  56. 943
      upb/lua/def.c
  57. 154
      upb/lua/lua_proto_library.bzl
  58. 96
      upb/lua/main.c
  59. 1118
      upb/lua/msg.c
  60. 98
      upb/lua/test.proto
  61. 852
      upb/lua/test_upb.lua
  62. 261
      upb/lua/upb.c
  63. 135
      upb/lua/upb.h
  64. 58
      upb/lua/upb.lua
  65. 139
      upb/lua/upbc.cc
  66. 188
      upb/protos/BUILD
  67. 43
      upb/protos/bazel/BUILD
  68. 307
      upb/protos/bazel/upb_cc_proto_library.bzl
  69. 187
      upb/protos/protos.cc
  70. 520
      upb/protos/protos.h
  71. 39
      upb/protos/protos_extension_lock.cc
  72. 54
      upb/protos/protos_extension_lock.h
  73. 140
      upb/protos/protos_extension_lock_test.cc
  74. 48
      upb/protos/protos_internal.h
  75. 60
      upb/protos/protos_internal_test.cc
  76. 44
      upb/protos/protos_traits.h
  77. 309
      upb/protos/repeated_field.h
  78. 356
      upb/protos/repeated_field_iterator.h
  79. 478
      upb/protos/repeated_field_iterator_test.cc
  80. 119
      upb/protos_generator/BUILD
  81. 584
      upb/protos_generator/gen_accessors.cc
  82. 51
      upb/protos_generator/gen_accessors.h
  83. 144
      upb/protos_generator/gen_enums.cc
  84. 52
      upb/protos_generator/gen_enums.h
  85. 117
      upb/protos_generator/gen_extensions.cc
  86. 54
      upb/protos_generator/gen_extensions.h
  87. 511
      upb/protos_generator/gen_messages.cc
  88. 51
      upb/protos_generator/gen_messages.h
  89. 347
      upb/protos_generator/gen_repeated_fields.cc
  90. 69
      upb/protos_generator/gen_repeated_fields.h
  91. 152
      upb/protos_generator/gen_utils.cc
  92. 68
      upb/protos_generator/gen_utils.h
  93. 201
      upb/protos_generator/names.cc
  94. 73
      upb/protos_generator/names.h
  95. 92
      upb/protos_generator/output.cc
  96. 174
      upb/protos_generator/output.h
  97. 282
      upb/protos_generator/protoc-gen-upb-protos.cc
  98. 157
      upb/protos_generator/tests/BUILD
  99. 49
      upb/protos_generator/tests/child_model.proto
  100. 40
      upb/protos_generator/tests/legacy-name.proto
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,16 @@
---
tasks:
ubuntu:
platform: ubuntu2004
shell_commands:
- "sudo apt -y update && sudo apt -y install libreadline-dev cmake rsync"
build_flags:
- "--incompatible_disallow_empty_glob"
test_targets:
- //...
macos:
platform: macos
build_flags:
- "--incompatible_disallow_empty_glob"
test_targets:
- //...

@ -0,0 +1,63 @@
# temporary fix for https://github.com/bazelbuild/bazel/issues/12905 on macOS
build --features=-debug_prefix_map_pwd_is_dot
# Pin to C++17
build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17
build:cpp17_msvc --cxxopt=/std:c++17 --host_cxxopt=/std:c++17
# Disallow empty globs
build: --incompatible_disallow_empty_glob
# Use our custom-configured c++ toolchain.
build:m32 --copt=-m32 --linkopt=-m32
build:asan --copt=-fsanitize=address --linkopt=-fsanitize=address
build:msan --copt=-fsanitize=memory --linkopt=-fsanitize=memory
# For Valgrind, we have to disable checks of "possible" leaks because the Python
# interpreter does the sorts of things that flag Valgrind "possible" leak checks.
# Ideally we could enforce a stricter check for the non-Python tests, but I don't
# know of an easy way to do that.
#
# We also have to disable pymalloc to avoid triggering Valgrind.
build:valgrind --run_under='valgrind --leak-check=full --track-origins=yes --trace-children=yes --show-leak-kinds=all --error-exitcode=1 --num-callers=500 ' --action_env=PYTHONMALLOC=malloc
build:ubsan --copt=-fsanitize=undefined --linkopt=-fsanitize=undefined --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1
# Workaround for the fact that Bazel links with $CC, not $CXX
# https://github.com/bazelbuild/bazel/issues/11122#issuecomment-613746748
build:ubsan --copt=-fno-sanitize=function --copt=-fno-sanitize=vptr
# Workaround for https://bugs.llvm.org/show_bug.cgi?id=16404
build:ubsan --linkopt=--rtlib=compiler-rt --linkopt=-lunwind
build:Werror --copt=-Werror
build:Werror --per_file_copt=json/parser@-Wno-error
build:Werror --per_file_copt=com_google_protobuf@-Wno-error
# GCC's -fanalyzer, a deeper static analysis than normal warnings.
build:analyzer --copt=-fanalyzer --copt=-Werror
build:analyzer --per_file_copt=json/parser@-fno-analyzer
build:analyzer --per_file_copt=com_google_protobuf@-fno-analyzer
build:analyzer --per_file_copt=com_github_google_benchmark@-fno-analyzer
# --config=asan-libfuzzer
build:asan-libfuzzer --action_env=CC=clang
build:asan-libfuzzer --action_env=CXX=clang++
build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
# --config=msan-libfuzzer
build:msan-libfuzzer --action_env=CC=clang
build:msan-libfuzzer --action_env=CXX=clang++
build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
build:msan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=msan
# --config=ubsan-libfuzzer
build:ubsan-libfuzzer --action_env=CC=clang
build:ubsan-libfuzzer --action_env=CXX=clang++
build:ubsan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
build:ubsan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
build:ubsan-libfuzzer --copt=-fsanitize=undefined
build:ubsan-libfuzzer --linkopt=-fsanitize=undefined
build:ubsan-libfuzzer --linkopt=-fsanitize-link-c++-runtime

@ -0,0 +1,3 @@
BasedOnStyle: Google
DerivePointerAlignment: false
PointerAlignment: Left

@ -0,0 +1,21 @@
name: "Setup Bazel Caching"
description: "Sets up Bazel caching"
inputs:
cache_url:
description: "URL of the Bazel cache to read/write"
required: false
default: https://storage.googleapis.com/protobuf-bazel-cache/upb
read_only:
description: "If true, we can read from the cache but not write it."
required: false
default: ${{ github.event.pull_request.head.repo.full_name != 'protocolbuffers/upb' }}
outputs:
cache_args:
description: "Caching related arguments to pass to 'bazel build"
value: --remote_cache=${{ inputs.cache_url }} ${{ steps.set_auth_arg.outputs.auth_arg }}
runs:
using: "composite"
steps:
- id: set_auth_arg
run: echo auth_arg=${{ inputs.read_only == 'true' && '--remote_upload_local_results=false' || '--google_default_credentials' }} >> $GITHUB_OUTPUT
shell: bash

@ -0,0 +1,107 @@
name: Bazel Tests
on:
push:
branches:
- main
- '[0-9]+.x'
pull_request:
branches:
- main
- '[0-9]+.x'
workflow_dispatch:
jobs:
ubuntu:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # Don't cancel all jobs if one fails.
matrix:
include:
- { NAME: "Fastbuild", BAZEL: bazel, CC: clang, os: ubuntu-20-large, flags: "" }
- { NAME: "Optimized", BAZEL: bazel, CC: clang, os: ubuntu-20-large, flags: "-c opt" } # Some warnings only fire with -c opt
- { NAME: "GCC Optimized", BAZEL: bazel, CC: gcc-12, os: ubuntu-22.04, flags: "-c opt" }
- { NAME: "FastTable", BAZEL: bazel, CC: clang, os: ubuntu-20-large, flags: "--//:fasttable_enabled=true -- -cmake:test_generated_files" }
- { NAME: "ASAN", BAZEL: bazel, CC: clang, os: ubuntu-20-large, flags: "--config=asan -c dbg -- -benchmarks:benchmark -python/..." }
- { NAME: "UBSAN", BAZEL: bazel, CC: clang, os: ubuntu-20-large, flags: "--config=ubsan -c dbg -- -benchmarks:benchmark -python/... -lua/...", install: "libunwind-dev" }
- { NAME: "32-bit", BAZEL: bazel, CC: clang, os: ubuntu-20-large, flags: "--copt=-m32 --linkopt=-m32 -- benchmarks:benchmark -python/...", install: "g++-multilib" }
- { NAME: "32-bit ASAN", BAZEL: bazel, CC: clang, os: ubuntu-20-large, flags: "--config=asan -c dbg --copt=-m32 --linkopt=-m32 -- -//benchmarks/... -//python/... -//upb/message:copy_test -//upb/message:promote_test -//upb/message:test -//upb/test:test_generated_code", install: "g++-multilib" }
- { NAME: "Windows", BAZEL: bazel, os: windows-2019, startup-flags: "--output_user_root=C:/tmp", flags: "--config=cpp17_msvc", targets: "upb/... upbc/... python/... protos/... protos_generator/..." }
- { NAME: "macOS", BAZEL: bazel, CC: clang, os: macos-11 }
# Current github runners are all Intel based, so just build/compile for Apple Silicon to detect issues there.
- { NAME: "macOS ARM (build only)", BAZEL: bazel, BAZEL_CMD: build, CC: clang, os: macos-11, flags: "--cpu=darwin_arm64"}
# We support two Bazel versions back per https://opensource.google/documentation/policies/cplusplus-support
- { NAME: "Bazel 5.3.0", BAZEL: bazel-5.3.0-linux-x86_64, CC: clang, os: ubuntu-20-large }
- { NAME: "Bazel 6.1.0", BAZEL: bazel-6.1.0-linux-x86_64, CC: clang, os: ubuntu-20-large }
name: ${{ matrix.NAME }}
steps:
- uses: actions/checkout@v2
- name: Set up Cloud SDK
uses: google-github-actions/auth@v0
with:
credentials_json: ${{ secrets.GOOGLE_CREDENTIALS }}
export_environment_variables: true
if: ${{ github.event.pull_request.head.repo.full_name == 'protocolbuffers/upb' }}
- name: Download historical Bazel version
run: |
FILENAME=$HOME/bin/${{ matrix.BAZEL }}
VERSION=$(echo ${{ matrix.BAZEL }} | cut -d- -f 2 )
mkdir -p $HOME/bin
echo $HOME/bin >> $GITHUB_PATH
wget -O $FILENAME https://github.com/bazelbuild/bazel/releases/download/$VERSION/${{ matrix.BAZEL }}
chmod a+x $FILENAME
if: ${{ matrix.BAZEL != 'bazel' }}
- name: Check compiler versions
if: matrix.CC
run: ${{ matrix.CC }} --version
- name: Check Bazel versions
run: ${{ matrix.BAZEL }} --version
- id: bazel-cache
name: Set up Bazel caching
uses: ./.github/actions/setup-bazel-cache
- name: Setup Python venv
if: ${{ runner.os != 'Windows' }}
run: rm -rf /tmp/venv && python3 -m venv /tmp/venv && source /tmp/venv/bin/activate && python3 --version
- name: Install dependencies
run: sudo apt update && sudo apt install -y ${{ matrix.install }}
if: matrix.install != ''
- name: Install numpy
run: pip3 install numpy
- name: Setup environment variables
if: matrix.CC
run: echo "CC=${{ matrix.CC }}" >> $GITHUB_ENV
- name: Run tests
run: cd ${{ github.workspace }} && ${{ matrix.BAZEL }} ${{ matrix.startup-flags }} ${{ matrix.BAZEL_CMD || 'test' }} --test_output=errors ${{ steps.bazel-cache.outputs.cache_args }} ${{ matrix.targets || '...' }} ${{ matrix.flags }}
- uses: actions/upload-artifact@v3
with:
name: logs
path: |
**/*.log
no-python:
runs-on: ubuntu-20-large
strategy:
fail-fast: false # Don't cancel all jobs if one fails.
name: "No System Python"
steps:
- uses: actions/checkout@v2
- name: Set up Cloud SDK
uses: google-github-actions/auth@v0
with:
credentials_json: ${{ secrets.GOOGLE_CREDENTIALS }}
export_environment_variables: true
if: ${{ github.event.pull_request.head.repo.full_name == 'protocolbuffers/upb' }}
- id: bazel-cache
name: Set up Bazel caching
uses: ./.github/actions/setup-bazel-cache
- name: Uninstall python
run: which python3 && sudo mv `which python3` /tmp && ! which python3
- name: Run tests
run: cd ${{ github.workspace }} && bazel test --test_output=errors ${{ steps.bazel-cache.outputs.cache_args }} //python/... -- -//python/dist:source_wheel

@ -0,0 +1,22 @@
name: Check ClangFormat
on:
push:
branches:
- main
- '[0-9]+.x'
pull_request:
branches:
- main
- '[0-9]+.x'
workflow_dispatch:
jobs:
check_clang_format:
runs-on: ubuntu-20-large
steps:
- uses: actions/checkout@v2
- name: Run ClangFormat
run: find . | grep -E '\.(c|h|cc)$' | grep -E -v '^./(third_party|cmake)' | xargs clang-format -i
- name: Check for differences
run: git diff --exit-code

@ -0,0 +1,24 @@
name: Generate Files
# After any push to the main branch, re-generate pregenerated files.
on:
push:
branches:
- main
- '[0-9]+.x'
jobs:
generate:
if: github.repository == 'protocolbuffers/upb'
runs-on: ubuntu-22-large
steps:
- uses: actions/checkout@v3
with:
# Note: this token has an expiration date, so if the workflow starts
# failing then you may need to generate a fresh token.
token: ${{ secrets.BOT_ACCESS_TOKEN }}
- name: Configure name and email address in Git
run: cd ${{ github.workspace }} && git config user.name "Protobuf Team Bot" && git config user.email "protobuf-team-bot@google.com"
- name: Commit and push update
run: cd ${{ github.workspace }} && ./cmake/push_auto_update.sh

@ -0,0 +1,15 @@
mergeable:
pull_requests:
label:
and:
- must_exclude:
regex: '^disposition/DO NOT MERGE'
message: 'Pull request marked not mergeable'
- must_include:
regex: 'mergeable:force-allow'
message: 'Pull requests should not be merged directly and should instead
be handled by Copybara.
To enable Github merges, add the `mergeable:force-allow` label and get a second
approval. This should almost never be used except for releases or as a break glass
measure after discussing with the team.'

@ -0,0 +1,173 @@
name: Python Tests
on:
push:
branches:
- main
- '[0-9]+.x'
pull_request:
branches:
- main
- '[0-9]+.x'
workflow_dispatch:
jobs:
build_wheels:
name: Build Wheels
runs-on: ubuntu-large
if: ${{ github.event.pull_request.head.repo.full_name == 'protocolbuffers/upb' }}
env:
# Bazel 5.4.0. Once we have moved to toolchains, we can update to Bazel 6.x.
DOCKER_IMAGE: us-docker.pkg.dev/protobuf-build/release-containers/linux/apple@sha256:bb1d14738449916d489c1cbb062508c4dca5bd265ea3e67a2628ae40912b9b00
steps:
- uses: actions/checkout@v2
- name: Set up Cloud SDK
uses: google-github-actions/auth@v0
with:
credentials_json: ${{ secrets.GOOGLE_CREDENTIALS }}
export_environment_variables: true
- name: Use gcloud CLI
run: gcloud info
- name: Configure Docker
run: gcloud auth configure-docker -q us-docker.pkg.dev
- name: Pull Docker Image
run: docker pull $DOCKER_IMAGE
- name: Check Bazel version
run: cd ${{ github.workspace }} && docker run -v$PWD:/workspace $DOCKER_IMAGE --version
- id: bazel-cache
name: Set up Bazel caching
uses: ./.github/actions/setup-bazel-cache
- name: Build Wheels
run: cd ${{ github.workspace }} && docker run -e GOOGLE_APPLICATION_CREDENTIALS=/workspace/$(basename $GOOGLE_APPLICATION_CREDENTIALS) -v$PWD:/workspace $DOCKER_IMAGE build $BAZEL_CACHE --crosstool_top=@com_google_protobuf//toolchain:clang_suite --@com_google_protobuf//toolchain:release=true --symlink_prefix=/ -c dbg python/dist ${{ steps.bazel-cache.outputs.cache_args }} python/dist:test_wheel python/dist:source_wheel
- name: Move Wheels
run: mkdir wheels && find _build/out \( -name 'protobuf*.whl' -o -name 'protobuf-*.tar.gz' \) -exec mv '{}' wheels ';'
- uses: actions/upload-artifact@v3
with:
name: python-wheels
path: wheels/
- uses: actions/upload-artifact@v3
with:
name: requirements
# Tests shouldn't have access to the whole upb repo, upload the one file we need
path: python/requirements.txt
test_wheels:
name: Test Wheels
needs: build_wheels
strategy:
fail-fast: false # Don't cancel all jobs if one fails.
matrix:
include:
# Linux and Mac use the limited API, so all Python versions will use a single wheel.
# As a result we can just test the oldest and newest supported Python versions and assume
# this gives us sufficient test coverage.
- { os: ubuntu-18-large, python-version: "3.7", architecture: x64, type: 'binary' }
- { os: macos-11, python-version: "3.7", architecture: x64, type: 'binary' }
- { os: ubuntu-20-large, python-version: "3.10", architecture: x64, type: 'binary' }
- { os: macos-12, python-version: "3.10", architecture: x64, type: 'binary' }
- { os: ubuntu-18-large, python-version: "3.7", architecture: x64, type: 'source' }
- { os: macos-11, python-version: "3.7", architecture: x64, type: 'source' }
- { os: ubuntu-20-large, python-version: "3.10", architecture: x64, type: 'source' }
- { os: macos-12, python-version: "3.10", architecture: x64, type: 'source' }
# Windows uses the full API up until Python 3.10, so each of these
# jobs tests a distinct binary wheel.
- { os: windows-2019-large, python-version: "3.7", architecture: x86, type: 'binary' }
- { os: windows-2019-large, python-version: "3.8", architecture: x86, type: 'binary' }
- { os: windows-2019-large, python-version: "3.9", architecture: x86, type: 'binary' }
- { os: windows-2019-large, python-version: "3.10", architecture: x86, type: 'binary' }
- { os: windows-2019-large, python-version: "3.7", architecture: x64, type: 'binary' }
- { os: windows-2019-large, python-version: "3.8", architecture: x64, type: 'binary' }
- { os: windows-2019-large, python-version: "3.9", architecture: x64, type: 'binary' }
- { os: windows-2019-large, python-version: "3.10", architecture: x64, type: 'binary' }
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: Download Wheels
uses: actions/download-artifact@v3
with:
name: python-wheels
path: wheels
- name: Download Requirements
uses: actions/download-artifact@v3
with:
name: requirements
path: requirements
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.architecture }}
- name: Setup Python venv
run: |
python -m pip install --upgrade pip
python -m venv env
# Windows uses 'Scripts' instead of 'bin'
source env/bin/activate || source env/Scripts/activate
echo "VIRTUAL ENV:" $VIRTUAL_ENV
- name: Install tzdata
run: pip install tzdata
# Only needed on Windows, Linux ships with tzdata.
if: ${{ contains(matrix.os, 'windows') }}
- name: Install requirements
run: pip install -r requirements/requirements.txt
- name: Install Protobuf Binary Wheel
run: pip install -vvv --no-index --find-links wheels protobuf
if: ${{ matrix.type == 'binary' }}
- name: Install Protobuf Source Wheel
run: |
cd wheels
tar -xzvf *.tar.gz
cd protobuf-*/
pip install .
if: ${{ matrix.type == 'source' }}
- name: Test that module is importable
run: python -v -c 'from google._upb import _message; assert "google._upb._message.MessageMeta" in str(_message.MessageMeta)'
if: ${{ !matrix.pure_python }}
- name: Install Protobuf Test Wheel
run: pip install -vvv --no-index --find-links wheels protobuftests
- name: Run the unit tests
run: |
TESTS=$(pip show -f protobuftests | grep pb_unit_tests.*py$ | sed 's,/,.,g' | sed 's,\\,.,g' | sed -E 's,.py$,,g')
for test in $TESTS; do
python -m unittest -v $test
done
test_pure_python_wheels:
name: Test Pure Python Wheels
needs: build_wheels
strategy:
fail-fast: false # Don't cancel all jobs if one fails.
matrix:
python-version: ["3.7", "3.10"]
runs-on: ubuntu-large
steps:
- name: Download Wheels
uses: actions/download-artifact@v3
with:
name: python-wheels
path: wheels
- name: Delete Binary Wheels
run: find wheels -type f | grep -v none-any | xargs rm
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Setup Python venv
run: |
python -m pip install --upgrade pip
python -m venv env
source env/bin/activate
echo "VIRTUAL ENV:" $VIRTUAL_ENV
- name: Install numpy
run: pip install numpy
- name: Install Protobuf Wheels
run: pip install -vvv --no-index --find-links wheels protobuf protobuftests
- name: Run the unit tests
run: |
TESTS=$(pip show -f protobuftests | grep _test.py | sed 's,/,.,g' | sed -E 's,.py$,,g')
for test in $TESTS; do
python -m unittest -v $test
done

6
upb/.gitignore vendored

@ -0,0 +1,6 @@
*.sw?
obj/
lib/
bazel-*
_build
.vscode

@ -0,0 +1,744 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load("@rules_python//python:defs.bzl", "py_binary")
load("//bazel:build_defs.bzl", "UPB_DEFAULT_COPTS")
load(
"//bazel:upb_proto_library.bzl",
"upb_proto_library_copts",
"upb_proto_reflection_library",
)
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load(
"//upbc:bootstrap_compiler.bzl",
"bootstrap_cc_library",
"bootstrap_upb_proto_library",
)
# begin:google_only
# load("//tools/build_defs/kotlin/native:rules.bzl", "kt_native_interop_hint")
# load("//tools/build_defs/license:license.bzl", "license")
# end:google_only
# begin:github_only
load(
"//bazel:amalgamation.bzl",
"upb_amalgamation",
)
# end:github_only
# begin:google_only
# package(default_applicable_licenses = ["//:license"])
#
# license(
# name = "license",
# package_name = "upb",
# )
# end:google_only
licenses(["notice"])
exports_files(["LICENSE"])
exports_files(
[
"BUILD",
"WORKSPACE",
],
visibility = ["//cmake:__pkg__"],
)
config_setting(
name = "windows",
constraint_values = ["@platforms//os:windows"],
visibility = ["//visibility:public"],
)
bool_flag(
name = "fasttable_enabled",
build_setting_default = False,
visibility = ["//visibility:public"],
)
config_setting(
name = "fasttable_enabled_setting",
flag_values = {"//:fasttable_enabled": "true"},
visibility = ["//visibility:public"],
)
upb_proto_library_copts(
name = "upb_proto_library_copts__for_generated_code_only_do_not_use",
copts = UPB_DEFAULT_COPTS,
visibility = ["//visibility:public"],
)
# Please update copy.bara.sky target = ":friends" if
# you make changes to this list.
package_group(
name = "friends",
packages = ["//..."],
)
# This is a stub library to keep gRPC happy. Do not use it for any reason,
# use the smaller targets below instead.
cc_library(
name = "upb",
hdrs = [
"upb/upb.hpp",
],
copts = UPB_DEFAULT_COPTS,
visibility = ["//visibility:public"],
deps = [
":base",
":mem",
],
)
# Common support routines used by generated code. This library has no
# implementation, but depends on :upb and exposes a few more hdrs.
#
# This is public only because we have no way of visibility-limiting it to
# upb_proto_library() only. This interface is not stable and by using it you
# give up any backward compatibility guarantees.
cc_library(
name = "generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
hdrs = ["upb/generated_code_support.h"],
copts = UPB_DEFAULT_COPTS,
textual_hdrs = [
"//upb/port:inc",
],
visibility = ["//visibility:public"],
deps = [
":base",
":collections",
":collections_internal",
":mem",
":message",
":message_accessors",
":message_accessors_internal",
":message_internal",
":mini_descriptor",
":mini_table",
":wire",
":wire_internal",
],
)
# Common support code for C++ generated code.
cc_library(
name = "generated_cpp_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
copts = UPB_DEFAULT_COPTS,
textual_hdrs = [
"//upb/port:inc",
],
visibility = ["//visibility:public"],
)
cc_library(
name = "generated_reflection_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
hdrs = [
"upb/reflection/def.h",
"upb/reflection/internal/def_pool.h",
],
copts = UPB_DEFAULT_COPTS,
textual_hdrs = [
"//upb/port:inc",
],
visibility = ["//visibility:public"],
deps = [
":mem",
":mini_descriptor",
":reflection_internal",
],
)
bootstrap_upb_proto_library(
name = "descriptor_upb_proto",
base_dir = "upb/reflection/",
# TODO(b/289127200): Export 'net/proto2/proto/descriptor.upb.h' and remove "-layering_check".
features = ["-layering_check"],
google3_src_files = ["net/proto2/proto/descriptor.proto"],
google3_src_rules = ["//net/proto2/proto:descriptor_proto_source"],
oss_src_files = ["google/protobuf/descriptor.proto"],
oss_src_rules = ["@com_google_protobuf//:descriptor_proto_srcs"],
oss_strip_prefix = "third_party/protobuf/github/bootstrap/src",
proto_lib_deps = ["@com_google_protobuf//:descriptor_proto"],
visibility = ["//visibility:public"],
)
upb_proto_reflection_library(
name = "descriptor_upb_proto_reflection",
visibility = ["//visibility:public"],
deps = ["@com_google_protobuf//:descriptor_proto"],
)
# TODO(b/232091617): Once we can delete the deprecated forwarding headers
# (= everything in upb/) we can move this build target down into reflection/
bootstrap_cc_library(
name = "reflection",
hdrs = [
"upb/reflection/def.h",
"upb/reflection/def.hpp",
"upb/reflection/message.h",
"upb/reflection/message.hpp",
],
bootstrap_deps = [":reflection_internal"],
copts = UPB_DEFAULT_COPTS,
visibility = ["//visibility:public"],
deps = [
":base",
":collections",
":mem",
":port",
],
)
bootstrap_cc_library(
name = "reflection_internal",
srcs = [
"upb/reflection/def_builder.c",
"upb/reflection/def_pool.c",
"upb/reflection/def_type.c",
"upb/reflection/desc_state.c",
"upb/reflection/enum_def.c",
"upb/reflection/enum_reserved_range.c",
"upb/reflection/enum_value_def.c",
"upb/reflection/extension_range.c",
"upb/reflection/field_def.c",
"upb/reflection/file_def.c",
"upb/reflection/message.c",
"upb/reflection/message_def.c",
"upb/reflection/message_reserved_range.c",
"upb/reflection/method_def.c",
"upb/reflection/oneof_def.c",
"upb/reflection/service_def.c",
],
hdrs = [
"upb/reflection/common.h",
"upb/reflection/def.h",
"upb/reflection/def.hpp",
"upb/reflection/def_pool.h",
"upb/reflection/def_type.h",
"upb/reflection/enum_def.h",
"upb/reflection/enum_reserved_range.h",
"upb/reflection/enum_value_def.h",
"upb/reflection/extension_range.h",
"upb/reflection/field_def.h",
"upb/reflection/file_def.h",
"upb/reflection/internal/def_builder.h",
"upb/reflection/internal/def_pool.h",
"upb/reflection/internal/desc_state.h",
"upb/reflection/internal/enum_def.h",
"upb/reflection/internal/enum_reserved_range.h",
"upb/reflection/internal/enum_value_def.h",
"upb/reflection/internal/extension_range.h",
"upb/reflection/internal/field_def.h",
"upb/reflection/internal/file_def.h",
"upb/reflection/internal/message_def.h",
"upb/reflection/internal/message_reserved_range.h",
"upb/reflection/internal/method_def.h",
"upb/reflection/internal/oneof_def.h",
"upb/reflection/internal/service_def.h",
"upb/reflection/message.h",
"upb/reflection/message.hpp",
"upb/reflection/message_def.h",
"upb/reflection/message_reserved_range.h",
"upb/reflection/method_def.h",
"upb/reflection/oneof_def.h",
"upb/reflection/service_def.h",
],
bootstrap_deps = [":descriptor_upb_proto"],
copts = UPB_DEFAULT_COPTS,
visibility = ["//visibility:public"],
deps = [
":base",
":collections",
":hash",
":mem",
":message",
":message_accessors",
":mini_descriptor",
":mini_descriptor_internal",
":mini_table",
":port",
],
)
# Aliases ######################################################################
# TODO(b/295870230): Remove these.
alias(
name = "base",
actual = "//upb/base",
visibility = ["//visibility:public"],
)
alias(
name = "base_internal",
actual = "//upb/base:internal",
visibility = ["//visibility:public"],
)
alias(
name = "collections",
actual = "//upb/collections",
visibility = ["//visibility:public"],
)
alias(
name = "collections_internal",
actual = "//upb/collections:internal",
visibility = ["//visibility:public"],
)
alias(
name = "collections_split64",
actual = "//upb/collections:split64",
visibility = ["//visibility:public"],
)
alias(
name = "hash",
actual = "//upb/hash",
visibility = ["//visibility:public"],
)
alias(
name = "json",
actual = "//upb/json",
visibility = ["//visibility:public"],
)
alias(
name = "lex",
actual = "//upb/lex",
visibility = ["//visibility:public"],
)
alias(
name = "mem",
actual = "//upb/mem",
visibility = ["//visibility:public"],
)
alias(
name = "mem_internal",
actual = "//upb/mem:internal",
visibility = ["//:__subpackages__"],
)
alias(
name = "message",
actual = "//upb/message",
visibility = ["//visibility:public"],
)
alias(
name = "message_accessors",
actual = "//upb/message:accessors",
visibility = ["//visibility:public"],
)
alias(
name = "message_accessors_internal",
actual = "//upb/message:accessors_internal",
visibility = ["//:friends"],
)
alias(
name = "message_copy",
actual = "//upb/message:copy",
visibility = ["//visibility:public"],
)
alias(
name = "message_internal",
actual = "//upb/message:internal",
visibility = ["//visibility:public"],
)
alias(
name = "message_promote",
actual = "//upb/message:promote",
visibility = ["//visibility:public"],
)
alias(
name = "message_rep_internal",
actual = "//upb/message:rep_internal",
visibility = ["//visibility:public"],
)
alias(
name = "message_split64",
actual = "//upb/message:split64",
visibility = ["//visibility:public"],
)
alias(
name = "message_tagged_ptr",
actual = "//upb/message:tagged_ptr",
visibility = ["//:friends"],
)
alias(
name = "message_types",
actual = "//upb/message:types",
visibility = ["//visibility:public"],
)
alias(
name = "mini_descriptor",
actual = "//upb/mini_descriptor",
visibility = ["//visibility:public"],
)
alias(
name = "mini_descriptor_internal",
actual = "//upb/mini_descriptor:internal",
visibility = ["//:__subpackages__"],
)
alias(
name = "mini_table",
actual = "//upb/mini_table",
visibility = ["//:friends"],
)
# begin:google_only
# alias(
# name = "mini_table_compat",
# actual = "//upb/mini_table:compat",
# compatible_with = ["//buildenv/target:non_prod"],
# visibility = ["//:friends"],
# )
# end:google_only
alias(
name = "mini_table_internal",
actual = "//upb/mini_table:internal",
visibility = ["//visibility:public"],
)
alias(
name = "port",
actual = "//upb/port",
visibility = ["//visibility:public"],
)
alias(
name = "text",
actual = "//upb/text",
visibility = ["//visibility:public"],
)
alias(
name = "wire",
actual = "//upb/wire",
visibility = ["//visibility:public"],
)
alias(
name = "wire_internal",
actual = "//upb/wire:internal",
visibility = ["//visibility:public"],
)
alias(
name = "wire_reader",
actual = "//upb/wire:reader",
visibility = ["//visibility:public"],
)
alias(
name = "wire_types",
actual = "//upb/wire:types",
visibility = ["//visibility:public"],
)
alias(
name = "eps_copy_input_stream",
actual = "//upb/wire:eps_copy_input_stream",
visibility = ["//visibility:public"],
)
# Tests ########################################################################
cc_test(
name = "def_builder_test",
srcs = [
"upb/reflection/common.h",
"upb/reflection/def_builder_test.cc",
"upb/reflection/def_type.h",
"upb/reflection/internal/def_builder.h",
],
deps = [
":descriptor_upb_proto",
":hash",
":mem",
":port",
":reflection",
":reflection_internal",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
# Internal C/C++ libraries #####################################################
cc_binary(
name = "libupb.so",
srcs = ["upb/upb_so.c"],
copts = UPB_DEFAULT_COPTS + ["-DUPB_BUILD_API"],
linkshared = 1,
linkstatic = 1,
visibility = ["//visibility:public"],
deps = [
":collections",
":collections_split64",
":mem",
":message",
":message_accessors",
":message_split64",
":mini_descriptor",
":mini_table",
":port",
],
)
# Amalgamation #################################################################
# begin:github_only
upb_amalgamation(
name = "gen_amalgamation",
outs = [
"upb.c",
"upb.h",
],
libs = [
":base",
":base_internal",
":collections_internal",
":descriptor_upb_proto",
":eps_copy_input_stream",
":generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
":hash",
":lex",
":mem",
":mem_internal",
":message",
":message_accessors",
":message_internal",
":message_rep_internal",
":message_tagged_ptr",
":message_types",
":mini_descriptor",
":mini_descriptor_internal",
":mini_table",
":mini_table_internal",
":port",
":reflection",
":reflection_internal",
":wire",
":wire_internal",
":wire_reader",
":wire_types",
],
strip_import_prefix = ["src"],
)
cc_library(
name = "amalgamation",
srcs = ["upb.c"],
hdrs = ["upb.h"],
copts = UPB_DEFAULT_COPTS,
deps = ["@utf8_range"],
)
upb_amalgamation(
name = "gen_php_amalgamation",
outs = [
"php-upb.c",
"php-upb.h",
],
libs = [
":base",
":base_internal",
":collections_internal",
":descriptor_upb_proto_reflection",
":descriptor_upb_proto",
":eps_copy_input_stream",
":generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
":hash",
":json",
":lex",
":mem",
":mem_internal",
":message",
":message_accessors",
":message_internal",
":message_rep_internal",
":message_tagged_ptr",
":message_types",
":mini_descriptor",
":mini_descriptor_internal",
":mini_table",
":mini_table_internal",
":port",
":reflection",
":reflection_internal",
":wire",
":wire_internal",
":wire_reader",
":wire_types",
],
prefix = "php-",
strip_import_prefix = ["src"],
visibility = ["@com_google_protobuf//php:__subpackages__"],
)
cc_library(
name = "php_amalgamation",
srcs = ["php-upb.c"],
hdrs = ["php-upb.h"],
copts = UPB_DEFAULT_COPTS,
deps = ["@utf8_range"],
)
upb_amalgamation(
name = "gen_ruby_amalgamation",
outs = [
"ruby-upb.c",
"ruby-upb.h",
],
libs = [
":base",
":base_internal",
":collections_internal",
":descriptor_upb_proto",
":eps_copy_input_stream",
":generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
":hash",
":json",
":lex",
":mem",
":mem_internal",
":message",
":message_accessors",
":message_internal",
":message_rep_internal",
":message_tagged_ptr",
":message_types",
":mini_descriptor",
":mini_descriptor_internal",
":mini_table",
":mini_table_internal",
":port",
":reflection",
":reflection_internal",
":wire",
":wire_internal",
":wire_reader",
":wire_types",
],
prefix = "ruby-",
strip_import_prefix = ["src"],
visibility = ["@com_google_protobuf//ruby:__subpackages__"],
)
cc_library(
name = "ruby_amalgamation",
srcs = ["ruby-upb.c"],
hdrs = ["ruby-upb.h"],
copts = UPB_DEFAULT_COPTS,
deps = ["@utf8_range"],
)
exports_files(
[
"third_party/lunit/console.lua",
"third_party/lunit/lunit.lua",
],
visibility = ["//lua:__pkg__"],
)
filegroup(
name = "source_files",
srcs = glob(
[
"upb/**/*.c",
"upb/**/*.h",
"upb/**/*.hpp",
],
exclude = [
"upb/**/conformance_upb.c",
"upb/reflection/stage0/**/*",
],
),
visibility = [
"//cmake:__pkg__",
"//python/dist:__pkg__",
]
)
# end:github_only
# begin:google_only
#
# py_binary(
# name = "update_check_runs",
# srcs = ["update_check_runs.py"],
# main = "update_check_runs.py",
# deps = [
# "//third_party/py/absl:app",
# "//third_party/py/absl/flags",
# ],
# )
#
# kt_native_interop_hint(
# name = "upb_kotlin_native_hint",
# compatible_with = ["//buildenv/target:non_prod"],
# headers_to_exclude = glob([
# "**/*.hpp",
# ]),
# kotlin_package = "upb",
# no_string_conversion = ["_upb_MiniTable_Build"],
# strict_enums = [
# "upb_CType",
# "upb_DecodeStatus",
# "upb_EncodeStatus",
# "upb_FieldType",
# "upb_FindUnknown_Status",
# "upb_GetExtension_Status",
# "upb_GetExtensionAsBytes_Status",
# "upb_Label",
# "upb_MapInsertStatus",
# "upb_UnknownToMessage_Status",
# "upb_WireType",
# ],
# visibility = ["//:__subpackages__"],
# )
#
# end:google_only

@ -0,0 +1,37 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Get in touch
If your idea will take you more than, say, 30 minutes to
implement, please get in touch first via the issue tracker
to touch base about your plan. That will give an
opportunity for early feedback and help avoid wasting your
time.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code Reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Community Guidelines
This project follows [Google's Open Source Community
Guidelines](https://opensource.google/conduct/).

@ -0,0 +1,26 @@
Copyright (c) 2009-2021, Google LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Google LLC nor the names of any other
contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL GOOGLE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,84 @@
# μpb: small, fast C protos
μpb (often written 'upb') is a small
[protobuf](https://github.com/protocolbuffers/protobuf) implementation written
in C.
upb is the core runtime for protobuf languages extensions in
[Ruby](https://github.com/protocolbuffers/protobuf/tree/master/ruby),
[PHP](https://github.com/protocolbuffers/protobuf/tree/master/php), and
[Python](https://github.com/protocolbuffers/upb/tree/main/python).
While upb offers a C API, the C API & ABI **are not stable**. For this reason,
upb is not generally offered as a C library for direct consumption, and there
are no releases.
## Features
upb has comparable speed to protobuf C++, but is an order of magnitude smaller
in code size.
Like the main protobuf implementation in C++, it supports:
- a generated API (in C)
- reflection
- binary & JSON wire formats
- text format serialization
- all standard features of protobufs (oneofs, maps, unknown fields, extensions,
etc.)
- full conformance with the protobuf conformance tests
upb also supports some features that C++ does not:
- **optional reflection:** generated messages are agnostic to whether
reflection will be linked in or not.
- **no global state:** no pre-main registration or other global state.
- **fast reflection-based parsing:** messages loaded at runtime parse
just as fast as compiled-in messages.
However there are a few features it does not support:
- text format parsing
- deep descriptor verification: upb's descriptor validation is not as exhaustive
as `protoc`.
## Install
For Ruby, use [RubyGems](https://rubygems.org/gems/google-protobuf):
```
$ gem install google-protobuf
```
For PHP, use [PECL](https://pecl.php.net/package/protobuf):
```
$ sudo pecl install protobuf
```
For Python, use [PyPI](https://pypi.org/project/protobuf/):
```
$ sudo pip install protobuf
```
Alternatively, you can build and install upb using
[vcpkg](https://github.com/microsoft/vcpkg/) dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install upb
The upb port in vcpkg is kept up to date by microsoft team members and community
contributors.
If the version is out of date, please
[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
vcpkg repository.
## Contributing
Please see [CONTRIBUTING.md](CONTRIBUTING.md).

@ -0,0 +1,86 @@
workspace(name = "upb")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("//bazel:workspace_deps.bzl", "upb_deps")
upb_deps()
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
protobuf_deps()
load("@utf8_range//:workspace_deps.bzl", "utf8_range_deps")
utf8_range_deps()
http_archive(
name = "lua",
build_file = "//bazel:lua.BUILD",
sha256 = "b9e2e4aad6789b3b63a056d442f7b39f0ecfca3ae0f1fc0ae4e9614401b69f4b",
strip_prefix = "lua-5.2.4",
urls = [
"https://mirror.bazel.build/www.lua.org/ftp/lua-5.2.4.tar.gz",
"https://www.lua.org/ftp/lua-5.2.4.tar.gz",
],
)
http_archive(
name = "com_github_google_benchmark",
urls = ["https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.zip"],
strip_prefix = "benchmark-0baacde3618ca617da95375e0af13ce1baadea47",
sha256 = "62e2f2e6d8a744d67e4bbc212fcfd06647080de4253c97ad5c6749e09faf2cb0",
)
http_archive(
name = "com_google_googleapis",
urls = ["https://github.com/googleapis/googleapis/archive/refs/heads/master.zip"],
build_file = "//benchmarks:BUILD.googleapis",
strip_prefix = "googleapis-master",
patch_cmds = ["find google -type f -name BUILD.bazel -delete"],
)
http_archive(
name = "com_google_absl",
sha256 = "e7fdfe0bed87702a22c5b73b6b5fe08bedd25f17d617e52df6061b0f47d480b0",
strip_prefix = "abseil-cpp-e6044634dd7caec2d79a13aecc9e765023768757",
urls = [
"https://github.com/abseil/abseil-cpp/archive/e6044634dd7caec2d79a13aecc9e765023768757.tar.gz"
],
)
http_archive(
name = "com_google_googletest",
sha256 = "730215d76eace9dd49bf74ce044e8daa065d175f1ac891cc1d6bb184ef94e565",
strip_prefix = "googletest-f53219cdcb7b084ef57414efea92ee5b71989558",
urls = [
"https://github.com/google/googletest/archive/f53219cdcb7b084ef57414efea92ee5b71989558.tar.gz" # 2023-03-16
],
)
load("@com_google_googletest//:googletest_deps.bzl", "googletest_deps")
googletest_deps()
load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
rules_pkg_dependencies()
load("//bazel:system_python.bzl", "system_python")
system_python(
name = "system_python",
minimum_python_version = "3.7",
)
load("@system_python//:register.bzl", "register_system_python")
register_system_python()
load("@system_python//:pip.bzl", "pip_parse")
pip_parse(
name="pip_deps",
requirements = "//python:requirements.txt",
requirements_overrides = {
"3.11": "//python:requirements_311.txt",
},
)
load("@pip_deps//:requirements.bzl", "install_deps")
install_deps()

@ -0,0 +1,63 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load("@rules_python//python:defs.bzl", "py_binary")
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
# begin:google_only
# package(default_applicable_licenses = ["//:license"])
# end:google_only
licenses(["notice"])
exports_files(
["workspace_deps.bzl"],
visibility = ["//cmake:__pkg__"],
)
py_binary(
name = "amalgamate",
srcs = ["amalgamate.py"],
visibility = ["//:__pkg__"],
)
# py_proto_library() is private rule, only intended for internal use by upb.
# Hopefully py_proto_library() will eventually be availble in rules_proto or
# another upstream package.
bzl_library(
name = "py_proto_library_bzl",
srcs = ["py_proto_library.bzl"],
)
bzl_library(
name = "upb_proto_library_bzl",
srcs = ["upb_proto_library.bzl"],
visibility = ["//visibility:public"],
deps = [
"@bazel_skylib//lib:paths",
"@bazel_tools//tools/cpp:toolchain_utils.bzl",
"@rules_proto//proto:defs",
],
)

@ -0,0 +1,134 @@
#!/usr/bin/python
#
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
import re
import os
INCLUDE_RE = re.compile('^#include "([^"]*)"$')
def parse_include(line):
match = INCLUDE_RE.match(line)
return match.groups()[0] if match else None
class Amalgamator:
def __init__(self, h_out, c_out):
self.include_paths = ["."]
self.included = set()
self.output_h = open(h_out, "w")
self.output_c = open(c_out, "w")
self.h_out = h_out.split("/")[-1]
def amalgamate(self, h_files, c_files):
self.h_files = set(h_files)
self.output_c.write("/* Amalgamated source file */\n")
self.output_c.write('#include "%s"\n' % (self.h_out))
if self.h_out == "ruby-upb.h":
self.output_h.write("// Ruby is still using proto3 enum semantics for proto2\n")
self.output_h.write("#define UPB_DISABLE_PROTO2_ENUM_CHECKING\n")
self.output_h.write("/* Amalgamated source file */\n")
port_def = self._find_include_file("upb/port/def.inc")
port_undef = self._find_include_file("upb/port/undef.inc")
self._process_file(port_def, self.output_h)
self._process_file(port_def, self.output_c)
for file in c_files:
self._process_file(file, self.output_c)
self._process_file(port_undef, self.output_h)
self._process_file(port_undef, self.output_c)
def _process_file(self, infile_name, outfile):
lines = open(infile_name).readlines()
has_copyright = lines[0].startswith(
"// Protocol Buffers - Google's data interchange format"
)
if has_copyright:
while not lines[0].startswith(
"// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH"
" DAMAGE"
):
lines.pop(0)
lines.pop(0)
for line in lines:
if not self._process_include(line):
outfile.write(line)
def _find_include_file(self, name):
for h_file in self.h_files:
if h_file.endswith(name):
return h_file
def _process_include(self, line):
include = parse_include(line)
if not include:
return False
if not (include.startswith("upb") or include.startswith("google")):
return False
if include and (include.endswith("port/def.inc") or include.endswith("port/undef.inc")):
# Skip, we handle this separately
return True
if include.endswith("hpp"):
# Skip, we don't support the amalgamation from C++.
return True
elif include in self.included:
return True
else:
# Include this upb header inline.
h_file = self._find_include_file(include)
if h_file:
self.h_files.remove(h_file)
self.included.add(include)
self._process_file(h_file, self.output_h)
return True
raise RuntimeError("Couldn't find include: " + include + ", h_files=" + repr(self.h_files))
# ---- main ----
c_out = sys.argv[1]
h_out = sys.argv[2]
amalgamator = Amalgamator(h_out, c_out)
c_files = []
h_files = []
for arg in sys.argv[3:]:
arg = arg.strip()
if arg.endswith(".h") or arg.endswith(".inc"):
h_files.append(arg)
else:
c_files.append(arg)
amalgamator.amalgamate(h_files, c_files)

@ -0,0 +1,85 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Internal rules for building upb."""
load(":upb_proto_library.bzl", "GeneratedSrcsInfo")
# upb_amalgamation() rule, with file_list aspect.
SrcList = provider(
fields = {
"srcs": "list of srcs",
},
)
def _file_list_aspect_impl(target, ctx):
if GeneratedSrcsInfo in target:
srcs = target[GeneratedSrcsInfo]
return [SrcList(srcs = srcs.srcs + srcs.hdrs)]
srcs = []
for src in ctx.rule.attr.srcs:
srcs += src.files.to_list()
for hdr in ctx.rule.attr.hdrs:
srcs += hdr.files.to_list()
for hdr in ctx.rule.attr.textual_hdrs:
srcs += hdr.files.to_list()
return [SrcList(srcs = srcs)]
_file_list_aspect = aspect(
implementation = _file_list_aspect_impl,
)
def _upb_amalgamation(ctx):
inputs = []
for lib in ctx.attr.libs:
inputs += lib[SrcList].srcs
srcs = [src for src in inputs if not src.path.endswith("hpp")]
ctx.actions.run(
inputs = inputs,
outputs = ctx.outputs.outs,
arguments = [f.path for f in ctx.outputs.outs] + [f.path for f in srcs],
progress_message = "Making amalgamation",
executable = ctx.executable._amalgamator,
)
return []
upb_amalgamation = rule(
attrs = {
"_amalgamator": attr.label(
executable = True,
cfg = "exec",
default = "//bazel:amalgamate",
),
"prefix": attr.string(
default = "",
),
"libs": attr.label_list(aspects = [_file_list_aspect]),
"outs": attr.output_list(),
"strip_import_prefix": attr.string_list(),
},
implementation = _upb_amalgamation,
)

@ -0,0 +1,101 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Internal rules for building upb."""
_DEFAULT_CPPOPTS = []
_DEFAULT_COPTS = []
# begin:github_only
_DEFAULT_CPPOPTS.extend([
"-Wextra",
# "-Wshorten-64-to-32", # not in GCC (and my Kokoro images doesn't have Clang)
"-Werror",
"-Wno-unused-parameter",
"-Wno-long-long",
])
_DEFAULT_COPTS.extend([
"-std=c99",
"-Wall",
"-Wstrict-prototypes",
# GCC (at least) emits spurious warnings for this that cannot be fixed
# without introducing redundant initialization (with runtime cost):
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635
#"-Wno-maybe-uninitialized",
])
# end:github_only
UPB_DEFAULT_CPPOPTS = select({
"//:windows": [],
"//conditions:default": _DEFAULT_CPPOPTS,
})
UPB_DEFAULT_COPTS = select({
"//:windows": [],
"//:fasttable_enabled_setting": ["-std=gnu99", "-DUPB_ENABLE_FASTTABLE"],
"//conditions:default": _DEFAULT_COPTS,
})
runfiles_init = """\
# --- begin runfiles.bash initialization v2 ---
# Copy-pasted from the Bazel Bash runfiles library v2.
set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
source "$0.runfiles/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v2 ---
"""
def _get_real_short_path(file):
# For some reason, files from other archives have short paths that look like:
# ../com_google_protobuf/google/protobuf/descriptor.proto
short_path = file.short_path
if short_path.startswith("../"):
second_slash = short_path.index("/", 3)
short_path = short_path[second_slash + 1:]
return short_path
def _get_real_root(file):
real_short_path = _get_real_short_path(file)
return file.path[:-len(real_short_path) - 1]
def _get_real_roots(files):
roots = {}
for file in files:
real_root = _get_real_root(file)
if real_root:
roots[real_root] = True
return roots.keys()
def make_shell_script(name, contents, out):
contents = contents.replace("$", "$$")
native.genrule(
name = "gen_" + name,
outs = [out],
cmd = "(cat <<'HEREDOC'\n%s\nHEREDOC\n) > $@" % contents,
)

@ -0,0 +1,127 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package(
default_visibility = ["//visibility:public"],
)
cc_library(
name = "liblua_headers",
hdrs = [
"src/lauxlib.h",
"src/lua.h",
"src/lua.hpp",
"src/luaconf.h",
"src/lualib.h",
],
defines = ["LUA_USE_LINUX"],
includes = ["src"],
)
cc_library(
name = "liblua",
srcs = [
"src/lapi.c",
"src/lapi.h",
"src/lauxlib.c",
"src/lauxlib.h",
"src/lbaselib.c",
"src/lbitlib.c",
"src/lcode.c",
"src/lcode.h",
"src/lcorolib.c",
"src/lctype.c",
"src/lctype.h",
"src/ldblib.c",
"src/ldebug.c",
"src/ldebug.h",
"src/ldo.c",
"src/ldo.h",
"src/ldump.c",
"src/lfunc.c",
"src/lfunc.h",
"src/lgc.c",
"src/lgc.h",
"src/linit.c",
"src/liolib.c",
"src/llex.c",
"src/llex.h",
"src/llimits.h",
"src/lmathlib.c",
"src/lmem.c",
"src/lmem.h",
"src/loadlib.c",
"src/lobject.c",
"src/lobject.h",
"src/lopcodes.c",
"src/lopcodes.h",
"src/loslib.c",
"src/lparser.c",
"src/lparser.h",
"src/lstate.c",
"src/lstate.h",
"src/lstring.c",
"src/lstring.h",
"src/lstrlib.c",
"src/ltable.c",
"src/ltable.h",
"src/ltablib.c",
"src/ltm.c",
"src/ltm.h",
"src/lundump.c",
"src/lundump.h",
"src/lvm.c",
"src/lvm.h",
"src/lzio.c",
"src/lzio.h",
],
hdrs = [
"src/lauxlib.h",
"src/lua.h",
"src/lua.hpp",
"src/luaconf.h",
"src/lualib.h",
],
defines = ["LUA_USE_LINUX"],
includes = ["src"],
linkopts = [
"-lm",
"-ldl",
],
)
cc_binary(
name = "lua",
srcs = [
"src/lua.c",
],
linkopts = [
"-lreadline",
"-rdynamic",
],
deps = [
":liblua",
],
)

@ -0,0 +1,40 @@
--- python/google/protobuf/internal/test_util.py
+++ python/google/protobuf/internal/test_util.py
@@ -39,6 +39,7 @@ __author__ = 'robinson@google.com (Will Robinson)'
import numbers
import operator
import os.path
+import pathlib
from google.protobuf import unittest_import_pb2
from google.protobuf import unittest_pb2
@@ -617,17 +618,22 @@ def ExpectAllFieldsSet(test_case, message):
message.default_import_enum)
+def _SearchUp(path, filename):
+ path = pathlib.Path(path).resolve()
+ for parent in [path] + list(path.parents):
+ file_path = parent / ('google/protobuf/testdata/' + filename)
+ if file_path.exists():
+ # Found it. Load the golden file from the testdata directory.
+ return file_path.open('rb')
+ return None
+
def GoldenFile(filename):
"""Finds the given golden file and returns a file object representing it."""
# Search up the directory tree looking for the C++ protobuf source code.
- path = '.'
- while os.path.exists(path):
- if os.path.exists(os.path.join(path, 'src/google/protobuf')):
- # Found it. Load the golden file from the testdata directory.
- full_path = os.path.join(path, 'src/google/protobuf/testdata', filename)
- return open(full_path, 'rb')
- path = os.path.join(path, '..')
+ f = _SearchUp('.', filename) or _SearchUp(__file__, filename)
+ if f:
+ return f
# Search internally.
path = '.'

@ -0,0 +1,157 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""An implementation of py_proto_library().
We have to implement this ourselves because there is currently no reasonable
py_proto_library() rule available for Bazel.
Our py_proto_library() is similar to how a real py_proto_library() should work.
But it hasn't been deeply tested or reviewed, and upb should not be in the
business of vending py_proto_library(), so we keep it private to upb.
"""
load("@bazel_skylib//lib:paths.bzl", "paths")
# begin:github_only
load("@rules_proto//proto:defs.bzl", "ProtoInfo")
# end:github_only
# Generic support code #########################################################
# begin:github_only
_is_google3 = False
# end:github_only
# begin:google_only
# _is_google3 = True
# end:google_only
def _get_real_short_path(file):
# For some reason, files from other archives have short paths that look like:
# ../com_google_protobuf/google/protobuf/descriptor.proto
short_path = file.short_path
if short_path.startswith("../"):
second_slash = short_path.index("/", 3)
short_path = short_path[second_slash + 1:]
# Sometimes it has another few prefixes like:
# _virtual_imports/any_proto/google/protobuf/any.proto
# benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto
# We want just google/protobuf/any.proto.
virtual_imports = "_virtual_imports/"
if virtual_imports in short_path:
short_path = short_path.split(virtual_imports)[1].split("/", 1)[1]
return short_path
def _get_real_root(ctx, file):
real_short_path = _get_real_short_path(file)
root = file.path[:-len(real_short_path) - 1]
if not _is_google3 and ctx.rule.attr.strip_import_prefix:
root = paths.join(root, ctx.rule.attr.strip_import_prefix[1:])
return root
def _generate_output_file(ctx, src, extension):
package = ctx.label.package
if not _is_google3:
strip_import_prefix = ctx.rule.attr.strip_import_prefix
if strip_import_prefix and strip_import_prefix != "/":
if not package.startswith(strip_import_prefix[1:]):
fail("%s does not begin with prefix %s" % (package, strip_import_prefix))
package = package[len(strip_import_prefix):]
real_short_path = _get_real_short_path(src)
real_short_path = paths.relativize(real_short_path, package)
output_filename = paths.replace_extension(real_short_path, extension)
ret = ctx.actions.declare_file(output_filename)
return ret
# py_proto_library() ###########################################################
def _py_proto_library_rule_impl(ctx):
# A real py_proto_library() should enforce this constraint.
# We don't bother for now, since it saves us some effort not to.
#
# if len(ctx.attr.deps) != 1:
# fail("only one deps dependency allowed.")
files = []
for dep in ctx.attr.deps:
files += dep[PyInfo].transitive_sources.to_list()
return [
DefaultInfo(files = depset(direct = files)),
]
def _py_proto_library_aspect_impl(target, ctx):
proto_info = target[ProtoInfo]
proto_sources = proto_info.direct_sources
srcs = [_generate_output_file(ctx, name, "_pb2.py") for name in proto_sources]
transitive_sets = proto_info.transitive_descriptor_sets.to_list()
ctx.actions.run(
inputs = depset(
direct = [proto_info.direct_descriptor_set],
transitive = [proto_info.transitive_descriptor_sets],
),
outputs = srcs,
executable = ctx.executable._protoc,
arguments = [
"--python_out=" + _get_real_root(ctx, srcs[0]),
"--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]),
] +
[_get_real_short_path(file) for file in proto_sources],
progress_message = "Generating Python protos for :" + ctx.label.name,
)
outs_depset = depset(srcs)
return [
PyInfo(transitive_sources = outs_depset),
]
_py_proto_library_aspect = aspect(
attrs = {
"_protoc": attr.label(
executable = True,
cfg = "exec",
default = "@com_google_protobuf//:protoc",
),
},
implementation = _py_proto_library_aspect_impl,
provides = [
PyInfo,
],
attr_aspects = ["deps"],
)
py_proto_library = rule(
output_to_genfiles = True,
implementation = _py_proto_library_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [_py_proto_library_aspect],
allow_rules = ["proto_library"],
providers = [ProtoInfo],
),
},
)

@ -0,0 +1,84 @@
"""Helper methods to download different python versions"""
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
limited_api_build_file = """
cc_library(
name = "python_headers",
hdrs = glob(["**/Include/**/*.h"]),
strip_include_prefix = "Python-{}/Include",
visibility = ["//visibility:public"],
)
"""
def python_source_archive(name, sha256):
"""Helper method to create a python_headers target that will work for linux and macos.
Args:
name: The name of the target, should be in the form python_{VERSION}
sha256: The sha256 of the python package for the specified version
"""
version = name.split("-")[1]
http_archive(
name = name,
urls = [
"https://www.python.org/ftp/python/{0}/Python-{0}.tgz"
.format(version),
],
sha256 = sha256,
build_file_content = limited_api_build_file.format(version),
patch_cmds = [
"echo '#define SIZEOF_WCHAR_T 4' > Python-{}/Include/pyconfig.h"
.format(version),
],
)
nuget_build_file = """
cc_import(
name = "python_full_api",
hdrs = glob(["**/*.h"]),
shared_library = "python{0}.dll",
interface_library = "libs/python{0}.lib",
visibility = ["@upb//python:__pkg__"],
)
cc_import(
name = "python_limited_api",
hdrs = glob(["**/*.h"]),
shared_library = "python{1}.dll",
interface_library = "libs/python{1}.lib",
visibility = ["@upb//python:__pkg__"],
)
"""
def python_nuget_package(name, sha256):
"""Helper method to create full and limited api dependencies for windows using nuget
Args:
name: The name of the target, should be in the form nuget_python_{CPU}_{VERSION}
sha256: The sha256 of the nuget package for that version
"""
cpu = name.split("_")[2]
version = name.split("_")[3]
full_api_lib_number = version.split(".")[0] + version.split(".")[1]
limited_api_lib_number = version.split(".")[0]
folder_name_dict = {
"i686": "pythonx86",
"x86-64": "python",
}
http_archive(
name = name,
urls = [
"https://www.nuget.org/api/v2/package/{}/{}"
.format(folder_name_dict[cpu], version),
],
sha256 = sha256,
strip_prefix = "tools",
build_file_content =
nuget_build_file.format(full_api_lib_number, limited_api_lib_number),
type = "zip",
patch_cmds = ["cp -r include/* ."],
)

@ -0,0 +1,293 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Repository rule for using Python 3.x headers from the system."""
# Mock out rules_python's pip.bzl for cases where no system python is found.
_mock_pip = """
def _pip_install_impl(repository_ctx):
repository_ctx.file("BUILD.bazel", '''
py_library(
name = "noop",
visibility = ["//visibility:public"],
)
''')
repository_ctx.file("requirements.bzl", '''
def install_deps(*args, **kwargs):
print("WARNING: could not install pip dependencies")
def requirement(*args, **kwargs):
return "@{}//:noop"
'''.format(repository_ctx.attr.name))
pip_install = repository_rule(
implementation = _pip_install_impl,
attrs = {
"requirements": attr.string(),
"requirements_overrides": attr.string_dict(),
"python_interpreter_target": attr.string(),
},
)
pip_parse = pip_install
"""
# Alias rules_python's pip.bzl for cases where a system python is found.
_alias_pip = """
load("@rules_python//python:pip.bzl", _pip_install = "pip_install", _pip_parse = "pip_parse")
def _get_requirements(requirements, requirements_overrides):
for version, override in requirements_overrides.items():
if version in "{python_version}":
requirements = override
break
return requirements
def pip_install(requirements, requirements_overrides={{}}, **kwargs):
_pip_install(
python_interpreter_target = "@{repo}//:interpreter",
requirements = _get_requirements(requirements, requirements_overrides),
**kwargs,
)
def pip_parse(requirements, requirements_overrides={{}}, **kwargs):
_pip_parse(
python_interpreter_target = "@{repo}//:interpreter",
requirements = _get_requirements(requirements, requirements_overrides),
**kwargs,
)
"""
_mock_fuzzing_py = """
def fuzzing_py_install_deps():
print("WARNING: could not install fuzzing_py dependencies")
"""
# Alias rules_fuzzing's requirements.bzl for cases where a system python is found.
_alias_fuzzing_py = """
load("@fuzzing_py_deps//:requirements.bzl", _fuzzing_py_install_deps = "install_deps")
def fuzzing_py_install_deps():
_fuzzing_py_install_deps()
"""
_build_file = """
load("@bazel_skylib//lib:selects.bzl", "selects")
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
load("@bazel_tools//tools/python:toolchain.bzl", "py_runtime_pair")
cc_library(
name = "python_headers",
hdrs = glob(["python/**/*.h"], allow_empty = True),
includes = ["python"],
visibility = ["//visibility:public"],
)
string_flag(
name = "internal_python_support",
build_setting_default = "{support}",
values = [
"None",
"Supported",
"Unsupported",
]
)
config_setting(
name = "none",
flag_values = {{
":internal_python_support": "None",
}},
visibility = ["//visibility:public"],
)
config_setting(
name = "supported",
flag_values = {{
":internal_python_support": "Supported",
}},
visibility = ["//visibility:public"],
)
config_setting(
name = "unsupported",
flag_values = {{
":internal_python_support": "Unsupported",
}},
visibility = ["//visibility:public"],
)
selects.config_setting_group(
name = "exists",
match_any = [":supported", ":unsupported"],
visibility = ["//visibility:public"],
)
sh_binary(
name = "interpreter",
srcs = ["interpreter"],
visibility = ["//visibility:public"],
)
py_runtime(
name = "py3_runtime",
interpreter_path = "{interpreter}",
python_version = "PY3",
)
py_runtime_pair(
name = "runtime_pair",
py3_runtime = ":py3_runtime",
)
toolchain(
name = "python_toolchain",
toolchain = ":runtime_pair",
toolchain_type = "@rules_python//python:toolchain_type",
)
"""
_register = """
def register_system_python():
native.register_toolchains("@{}//:python_toolchain")
"""
_mock_register = """
def register_system_python():
pass
"""
def _get_python_version(repository_ctx):
py_program = "import sys; print(str(sys.version_info.major) + '.' + str(sys.version_info.minor) + '.' + str(sys.version_info.micro))"
result = repository_ctx.execute(["python3", "-c", py_program])
return (result.stdout).strip().split(".")
def _get_python_path(repository_ctx):
py_program = "import sysconfig; print(sysconfig.get_config_var('%s'), end='')"
result = repository_ctx.execute(["python3", "-c", py_program % ("INCLUDEPY")])
if result.return_code != 0:
return None
return result.stdout
def _populate_package(ctx, path, python3, python_version):
ctx.symlink(path, "python")
supported = True
for idx, v in enumerate(ctx.attr.minimum_python_version.split(".")):
if int(python_version[idx]) < int(v):
supported = False
break
if "win" in ctx.os.name:
# buildifier: disable=print
print("WARNING: python is not supported on Windows")
supported = False
build_file = _build_file.format(
interpreter = python3,
support = "Supported" if supported else "Unsupported",
)
ctx.file("interpreter", "#!/bin/sh\nexec {} \"$@\"".format(python3))
ctx.file("BUILD.bazel", build_file)
ctx.file("version.bzl", "SYSTEM_PYTHON_VERSION = '{}{}'".format(python_version[0], python_version[1]))
ctx.file("register.bzl", _register.format(ctx.attr.name))
if supported:
ctx.file("pip.bzl", _alias_pip.format(
python_version = ".".join(python_version),
repo = ctx.attr.name,
))
ctx.file("fuzzing_py.bzl", _alias_fuzzing_py)
else:
# Dependencies are unlikely to be satisfiable for unsupported versions of python.
ctx.file("pip.bzl", _mock_pip)
ctx.file("fuzzing_py.bzl", _mock_fuzzing_py)
def _populate_empty_package(ctx):
# Mock out all the entrypoints we need to run from WORKSPACE. Targets that
# actually need python should use `target_compatible_with` and the generated
# @system_python//:exists or @system_python//:supported constraints.
ctx.file(
"BUILD.bazel",
_build_file.format(
interpreter = "",
support = "None",
),
)
ctx.file("version.bzl", "SYSTEM_PYTHON_VERSION = 'None'")
ctx.file("register.bzl", _mock_register)
ctx.file("pip.bzl", _mock_pip)
ctx.file("fuzzing_py.bzl", _mock_fuzzing_py)
def _system_python_impl(repository_ctx):
path = _get_python_path(repository_ctx)
python3 = repository_ctx.which("python3")
python_version = _get_python_version(repository_ctx)
if path and python_version[0] == "3":
_populate_package(repository_ctx, path, python3, python_version)
else:
# buildifier: disable=print
print("WARNING: no system python available, builds against system python will fail")
_populate_empty_package(repository_ctx)
# The system_python() repository rule exposes information from the version of python installed in the current system.
#
# In WORKSPACE:
# system_python(
# name = "system_python_repo",
# minimum_python_version = "3.7",
# )
#
# This repository exposes some repository rules for configuring python in Bazel. The python toolchain
# *must* be registered in your WORKSPACE:
# load("@system_python_repo//:register.bzl", "register_system_python")
# register_system_python()
#
# Pip dependencies can optionally be specified using a wrapper around rules_python's repository rules:
# load("@system_python//:pip.bzl", "pip_install")
# pip_install(
# name="pip_deps",
# requirements = "@com_google_protobuf//python:requirements.txt",
# )
# An optional argument `requirements_overrides` takes a dictionary mapping python versions to alternate
# requirements files. This works around the requirement for fully pinned dependencies in python_rules.
#
# Four config settings are exposed from this repository to help declare target compatibility in Bazel.
# For example, `@system_python_repo//:exists` will be true if a system python version has been found.
# The `none` setting will be true only if no python version was found, and `supported`/`unsupported`
# correspond to whether or not the system version is compatible with `minimum_python_version`.
#
# This repository also exposes a header rule that you can depend on from BUILD files:
# cc_library(
# name = "foobar",
# srcs = ["foobar.cc"],
# deps = ["@system_python_repo//:python_headers"],
# )
#
# The headers should correspond to the version of python obtained by running
# the `python3` command on the system.
system_python = repository_rule(
implementation = _system_python_impl,
local = True,
attrs = {
"minimum_python_version": attr.string(default = "3.7"),
},
)

@ -0,0 +1,530 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Public rules for using upb protos:
- upb_proto_library()
- upb_proto_reflection_library()
"""
load("@bazel_skylib//lib:paths.bzl", "paths")
# begin:google_only
# load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
# end:google_only
# begin:github_only
# Compatibility code for Bazel 4.x. Remove this when we drop support for Bazel 4.x.
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
def use_cpp_toolchain():
return ["@bazel_tools//tools/cpp:toolchain_type"]
# end:github_only
# Generic support code #########################################################
# begin:github_only
_is_google3 = False
# end:github_only
# begin:google_only
# _is_google3 = True
# end:google_only
def _get_real_short_path(file):
# For some reason, files from other archives have short paths that look like:
# ../com_google_protobuf/google/protobuf/descriptor.proto
short_path = file.short_path
if short_path.startswith("../"):
second_slash = short_path.index("/", 3)
short_path = short_path[second_slash + 1:]
# Sometimes it has another few prefixes like:
# _virtual_imports/any_proto/google/protobuf/any.proto
# benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto
# We want just google/protobuf/any.proto.
virtual_imports = "_virtual_imports/"
if virtual_imports in short_path:
short_path = short_path.split(virtual_imports)[1].split("/", 1)[1]
return short_path
def _get_real_root(ctx, file):
real_short_path = _get_real_short_path(file)
root = file.path[:-len(real_short_path) - 1]
if not _is_google3 and ctx.rule.attr.strip_import_prefix:
root = paths.join(root, ctx.rule.attr.strip_import_prefix[1:])
return root
def _generate_output_file(ctx, src, extension):
package = ctx.label.package
if not _is_google3:
strip_import_prefix = ctx.rule.attr.strip_import_prefix
if strip_import_prefix and strip_import_prefix != "/":
if not package.startswith(strip_import_prefix[1:]):
fail("%s does not begin with prefix %s" % (package, strip_import_prefix))
package = package[len(strip_import_prefix):]
real_short_path = _get_real_short_path(src)
real_short_path = paths.relativize(real_short_path, package)
output_filename = paths.replace_extension(real_short_path, extension)
ret = ctx.actions.declare_file(output_filename)
return ret
def _generate_include_path(src, out, extension):
short_path = _get_real_short_path(src)
short_path = paths.replace_extension(short_path, extension)
if not out.path.endswith(short_path):
fail("%s does not end with %s" % (out.path, short_path))
return out.path[:-len(short_path)]
def _filter_none(elems):
out = []
for elem in elems:
if elem:
out.append(elem)
return out
def _cc_library_func(ctx, name, hdrs, srcs, copts, includes, dep_ccinfos):
"""Like cc_library(), but callable from rules.
Args:
ctx: Rule context.
name: Unique name used to generate output files.
hdrs: Public headers that can be #included from other rules.
srcs: C/C++ source files.
copts: Additional options for cc compilation.
includes: Additional include paths.
dep_ccinfos: CcInfo providers of dependencies we should build/link against.
Returns:
CcInfo provider for this compilation.
"""
compilation_contexts = [info.compilation_context for info in dep_ccinfos]
linking_contexts = [info.linking_context for info in dep_ccinfos]
toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
(compilation_context, compilation_outputs) = cc_common.compile(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = toolchain,
name = name,
srcs = srcs,
includes = includes,
public_hdrs = hdrs,
user_compile_flags = copts,
compilation_contexts = compilation_contexts,
)
# buildifier: disable=unused-variable
(linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs(
actions = ctx.actions,
name = name,
feature_configuration = feature_configuration,
cc_toolchain = toolchain,
compilation_outputs = compilation_outputs,
linking_contexts = linking_contexts,
disallow_dynamic_library = cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows"),
)
return CcInfo(
compilation_context = compilation_context,
linking_context = linking_context,
)
# Dummy rule to expose select() copts to aspects ##############################
UpbProtoLibraryCoptsInfo = provider(
"Provides copts for upb proto targets",
fields = {
"copts": "copts for upb_proto_library()",
},
)
def upb_proto_library_copts_impl(ctx):
return UpbProtoLibraryCoptsInfo(copts = ctx.attr.copts)
upb_proto_library_copts = rule(
implementation = upb_proto_library_copts_impl,
attrs = {"copts": attr.string_list(default = [])},
)
# upb_proto_library / upb_proto_reflection_library shared code #################
GeneratedSrcsInfo = provider(
"Provides generated headers and sources",
fields = {
"srcs": "list of srcs",
"hdrs": "list of hdrs",
"thunks": "Experimental, do not use. List of srcs defining C API. Incompatible with hdrs.",
"includes": "list of extra includes",
},
)
def _concat_lists(lists):
ret = []
for lst in lists:
ret = ret + lst
return ret
def _merge_generated_srcs(srcs):
return GeneratedSrcsInfo(
srcs = _concat_lists([s.srcs for s in srcs]),
hdrs = _concat_lists([s.hdrs for s in srcs]),
thunks = _concat_lists([s.thunks for s in srcs]),
includes = _concat_lists([s.includes for s in srcs]),
)
UpbWrappedCcInfo = provider("Provider for cc_info for protos", fields = ["cc_info", "cc_info_with_thunks"])
def _merge_wrapped_cc_infos(infos, cc_infos):
return UpbWrappedCcInfo(
cc_info = cc_common.merge_cc_infos(
direct_cc_infos = cc_infos + [info.cc_info for info in infos],
),
cc_info_with_thunks = cc_common.merge_cc_infos(
direct_cc_infos = [info.cc_info_with_thunks for info in infos],
),
)
_UpbDefsWrappedCcInfo = provider("Provider for cc_info for protos", fields = ["cc_info"])
_UpbWrappedGeneratedSrcsInfo = provider("Provider for generated sources", fields = ["srcs"])
_WrappedDefsGeneratedSrcsInfo = provider(
"Provider for generated reflective sources",
fields = ["srcs"],
)
def _generate_upb_protos(ctx, generator, proto_info, proto_sources):
if len(proto_sources) == 0:
return GeneratedSrcsInfo(srcs = [], hdrs = [], thunks = [], includes = [])
ext = "." + generator
tool = getattr(ctx.executable, "_gen_" + generator)
srcs = [_generate_output_file(ctx, name, ext + ".c") for name in proto_sources]
hdrs = [_generate_output_file(ctx, name, ext + ".h") for name in proto_sources]
thunks = []
if generator == "upb":
thunks = [_generate_output_file(ctx, name, ext + ".thunks.c") for name in proto_sources]
transitive_sets = proto_info.transitive_descriptor_sets.to_list()
args = ctx.actions.args()
args.use_param_file(param_file_arg = "@%s")
args.set_param_file_format("multiline")
args.add("--" + generator + "_out=" + _get_real_root(ctx, srcs[0]))
args.add("--plugin=protoc-gen-" + generator + "=" + tool.path)
args.add("--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]))
args.add_all(proto_sources, map_each = _get_real_short_path)
ctx.actions.run(
inputs = depset(
direct = [proto_info.direct_descriptor_set],
transitive = [proto_info.transitive_descriptor_sets],
),
tools = [tool],
outputs = srcs + hdrs,
executable = ctx.executable._protoc,
arguments = [args],
progress_message = "Generating upb protos for :" + ctx.label.name,
mnemonic = "GenUpbProtos",
)
if generator == "upb":
ctx.actions.run_shell(
inputs = hdrs,
outputs = thunks,
command = " && ".join([
"sed 's/UPB_INLINE //' {} > {}".format(hdr.path, thunk.path)
for (hdr, thunk) in zip(hdrs, thunks)
]),
progress_message = "Generating thunks for upb protos API for: " + ctx.label.name,
mnemonic = "GenUpbProtosThunks",
)
return GeneratedSrcsInfo(
srcs = srcs,
hdrs = hdrs,
thunks = thunks,
includes = [_generate_include_path(proto_sources[0], hdrs[0], ext + ".h")],
)
def _upb_proto_rule_impl(ctx):
if len(ctx.attr.deps) != 1:
fail("only one deps dependency allowed.")
dep = ctx.attr.deps[0]
if _WrappedDefsGeneratedSrcsInfo in dep:
srcs = dep[_WrappedDefsGeneratedSrcsInfo].srcs
elif _UpbWrappedGeneratedSrcsInfo in dep:
srcs = dep[_UpbWrappedGeneratedSrcsInfo].srcs
else:
fail("proto_library rule must generate _UpbWrappedGeneratedSrcsInfo or " +
"_WrappedDefsGeneratedSrcsInfo (aspect should have handled this).")
if _UpbDefsWrappedCcInfo in dep:
cc_info = dep[_UpbDefsWrappedCcInfo].cc_info
elif UpbWrappedCcInfo in dep:
cc_info = dep[UpbWrappedCcInfo].cc_info
else:
fail("proto_library rule must generate UpbWrappedCcInfo or " +
"_UpbDefsWrappedCcInfo (aspect should have handled this).")
lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0]
files = _filter_none([
lib.static_library,
lib.pic_static_library,
lib.dynamic_library,
])
return [
DefaultInfo(files = depset(files + srcs.hdrs + srcs.srcs)),
srcs,
cc_info,
]
def _generate_name(ctx, generator, thunks = False):
if thunks:
return ctx.rule.attr.name + "." + generator + ".thunks"
return ctx.rule.attr.name + "." + generator
def _get_dep_cc_info(target, ctx, generator):
deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator)
dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep]
dep_ccinfos += [dep[_UpbDefsWrappedCcInfo].cc_info for dep in deps if _UpbDefsWrappedCcInfo in dep]
dep_wrapped_infos = [dep[UpbWrappedCcInfo] for dep in deps if UpbWrappedCcInfo in dep]
if generator == "upbdefs":
if UpbWrappedCcInfo not in target:
fail("Target should have UpbWrappedCcInfo provider")
dep_wrapped_infos.append(target[UpbWrappedCcInfo])
return _merge_wrapped_cc_infos(dep_wrapped_infos, dep_ccinfos)
def _compile_upb_protos(ctx, files, generator, dep_wrapped_ccinfo, cc_provider):
cc_info = _cc_library_func(
ctx = ctx,
name = _generate_name(ctx, generator),
hdrs = files.hdrs,
srcs = files.srcs,
includes = files.includes,
copts = ctx.attr._copts[UpbProtoLibraryCoptsInfo].copts,
dep_ccinfos = [dep_wrapped_ccinfo.cc_info],
)
if files.thunks:
cc_info_with_thunks = _cc_library_func(
ctx = ctx,
name = _generate_name(ctx, generator, files.thunks),
hdrs = [],
srcs = files.thunks,
includes = files.includes,
copts = ctx.attr._copts[UpbProtoLibraryCoptsInfo].copts,
dep_ccinfos = [dep_wrapped_ccinfo.cc_info, cc_info],
)
return cc_provider(
cc_info = cc_info,
cc_info_with_thunks = cc_info_with_thunks,
)
else:
return cc_provider(
cc_info = cc_info,
)
def _get_hint_providers(ctx, generator):
if generator not in _GENERATORS:
fail("Please add new generator '{}' to _GENERATORS list".format(generator))
possible_owners = []
for generator in _GENERATORS:
possible_owners.append(ctx.label.relative(_generate_name(ctx, generator)))
possible_owners.append(ctx.label.relative(_generate_name(ctx, generator, thunks = True)))
if hasattr(cc_common, "CcSharedLibraryHintInfo"):
return [cc_common.CcSharedLibraryHintInfo(owners = possible_owners)]
elif hasattr(cc_common, "CcSharedLibraryHintInfo_6_X_constructor_do_not_use"):
# This branch can be deleted once 6.X is not supported by upb rules
return [cc_common.CcSharedLibraryHintInfo_6_X_constructor_do_not_use(owners = possible_owners)]
return []
def _upb_proto_aspect_impl(target, ctx, generator, cc_provider, file_provider, provide_cc_shared_library_hints = True):
dep_wrapped_ccinfo = _get_dep_cc_info(target, ctx, generator)
if not getattr(ctx.rule.attr, "srcs", []):
# This target doesn't declare any sources, reexport all its deps instead.
# This is known as an "alias library":
# https://bazel.build/reference/be/protocol-buffer#proto_library.srcs
files = _merge_generated_srcs([dep[file_provider].srcs for dep in ctx.rule.attr.deps])
wrapped_cc_info = dep_wrapped_ccinfo
else:
proto_info = target[ProtoInfo]
files = _generate_upb_protos(
ctx,
generator,
proto_info,
proto_info.direct_sources,
)
wrapped_cc_info = _compile_upb_protos(
ctx,
files,
generator,
dep_wrapped_ccinfo,
cc_provider,
)
hints = _get_hint_providers(ctx, generator) if provide_cc_shared_library_hints else []
return hints + [
file_provider(srcs = files),
wrapped_cc_info,
]
_GENERATORS = ["upb", "upbdefs"]
def upb_proto_library_aspect_impl(target, ctx):
return _upb_proto_aspect_impl(target, ctx, "upb", UpbWrappedCcInfo, _UpbWrappedGeneratedSrcsInfo)
def _upb_proto_reflection_library_aspect_impl(target, ctx):
return _upb_proto_aspect_impl(target, ctx, "upbdefs", _UpbDefsWrappedCcInfo, _WrappedDefsGeneratedSrcsInfo, provide_cc_shared_library_hints = False)
# upb_proto_library() ##########################################################
def _get_upb_proto_library_aspect_provides():
provides = [
UpbWrappedCcInfo,
_UpbWrappedGeneratedSrcsInfo,
]
if hasattr(cc_common, "CcSharedLibraryHintInfo"):
provides.append(cc_common.CcSharedLibraryHintInfo)
elif hasattr(cc_common, "CcSharedLibraryHintInfo_6_X_getter_do_not_use"):
# This branch can be deleted once 6.X is not supported by upb rules
provides.append(cc_common.CcSharedLibraryHintInfo_6_X_getter_do_not_use)
return provides
upb_proto_library_aspect = aspect(
attrs = {
"_copts": attr.label(
default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use",
),
"_gen_upb": attr.label(
executable = True,
cfg = "exec",
default = "//upbc:protoc-gen-upb_stage1",
),
"_protoc": attr.label(
executable = True,
cfg = "exec",
default = "@com_google_protobuf//:protoc",
),
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"_upb": attr.label_list(default = [
"//:generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
]),
"_fasttable_enabled": attr.label(default = "//:fasttable_enabled"),
},
implementation = upb_proto_library_aspect_impl,
provides = _get_upb_proto_library_aspect_provides(),
attr_aspects = ["deps"],
fragments = ["cpp"],
toolchains = use_cpp_toolchain(),
incompatible_use_toolchain_transition = True,
)
upb_proto_library = rule(
output_to_genfiles = True,
implementation = _upb_proto_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [upb_proto_library_aspect],
allow_rules = ["proto_library"],
providers = [ProtoInfo],
),
},
provides = [CcInfo],
)
# upb_proto_reflection_library() ###############################################
_upb_proto_reflection_library_aspect = aspect(
attrs = {
"_copts": attr.label(
default = "//:upb_proto_library_copts__for_generated_code_only_do_not_use",
),
"_gen_upbdefs": attr.label(
executable = True,
cfg = "exec",
default = "//upbc:protoc-gen-upbdefs",
),
"_protoc": attr.label(
executable = True,
cfg = "exec",
default = "@com_google_protobuf//:protoc",
),
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"_upbdefs": attr.label_list(
default = [
"//:generated_reflection_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
],
),
},
implementation = _upb_proto_reflection_library_aspect_impl,
provides = [
_UpbDefsWrappedCcInfo,
_WrappedDefsGeneratedSrcsInfo,
],
required_aspect_providers = [
UpbWrappedCcInfo,
_UpbWrappedGeneratedSrcsInfo,
],
attr_aspects = ["deps"],
fragments = ["cpp"],
toolchains = use_cpp_toolchain(),
incompatible_use_toolchain_transition = True,
)
upb_proto_reflection_library = rule(
output_to_genfiles = True,
implementation = _upb_proto_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [
upb_proto_library_aspect,
_upb_proto_reflection_library_aspect,
],
allow_rules = ["proto_library"],
providers = [ProtoInfo],
),
},
provides = [CcInfo],
)

@ -0,0 +1,104 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
load("//bazel:python_downloads.bzl", "python_nuget_package", "python_source_archive")
def _github_archive(repo, commit, **kwargs):
repo_name = repo.split("/")[-1]
http_archive(
urls = [repo + "/archive/" + commit + ".zip"],
strip_prefix = repo_name + "-" + commit,
**kwargs
)
def upb_deps():
maybe(
_github_archive,
name = "com_google_absl",
repo = "https://github.com/abseil/abseil-cpp",
commit = "c2435f8342c2d0ed8101cb43adfd605fdc52dca2", # Abseil LTS 20230125.3
sha256 = "ea1d31db00eb37e607bfda17ffac09064670ddf05da067944c4766f517876390",
)
maybe(
_github_archive,
name = "com_google_protobuf",
repo = "https://github.com/protocolbuffers/protobuf",
commit = "22e845e279bd79ad013bff4b79660b8c8b72d935",
sha256 = "276215041e767973f274299783b5d7b7de1a3c55628b9890bd9eb064dfa5daaf",
patches = ["@upb//bazel:protobuf.patch"],
)
maybe(
_github_archive,
name = "utf8_range",
repo = "https://github.com/protocolbuffers/utf8_range",
commit = "de0b4a8ff9b5d4c98108bdfe723291a33c52c54f",
sha256 = "5da960e5e5d92394c809629a03af3c7709d2d3d0ca731dacb3a9fb4bf28f7702",
)
maybe(
http_archive,
name = "rules_pkg",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz",
"https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz",
],
sha256 = "8a298e832762eda1830597d64fe7db58178aa84cd5926d76d5b744d6558941c2",
)
maybe(
_github_archive,
name = "rules_python",
repo = "https://github.com/bazelbuild/rules_python",
commit = "912a5051f51581784fd64094f6bdabf93f6d698f", # 0.14.0
sha256 = "a3e4b4ade7c4a52e757b16a16e94d0b2640333062180cba577d81fac087a501d",
)
maybe(
http_archive,
name = "bazel_skylib",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
],
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
)
#Python Downloads
python_source_archive(
name = "python-3.7.0",
sha256 = "85bb9feb6863e04fb1700b018d9d42d1caac178559ffa453d7e6a436e259fd0d",
)
python_nuget_package(
name = "nuget_python_i686_3.7.0",
sha256 = "a8bb49fa1ca62ad55430fcafaca1b58015e22943e66b1a87d5e7cef2556c6a54",
)
python_nuget_package(
name = "nuget_python_x86-64_3.7.0",
sha256 = "66eb796a5bdb1e6787b8f655a1237a6b6964af2115b7627cf4f0032cf068b4b2",
)
python_nuget_package(
name = "nuget_python_i686_3.8.0",
sha256 = "87a6481f5eef30b42ac12c93f06f73bd0b8692f26313b76a6615d1641c4e7bca",
)
python_nuget_package(
name = "nuget_python_x86-64_3.8.0",
sha256 = "96c61321ce90dd053c8a04f305a5f6cc6d91350b862db34440e4a4f069b708a0",
)
python_nuget_package(
name = "nuget_python_i686_3.9.0",
sha256 = "229abecbe49dc08fe5709e0b31e70edfb3b88f23335ebfc2904c44f940fd59b6",
)
python_nuget_package(
name = "nuget_python_x86-64_3.9.0",
sha256 = "6af58a733e7dfbfcdd50d55788134393d6ffe7ab8270effbf724bdb786558832",
)
python_nuget_package(
name = "nuget_python_i686_3.10.0",
sha256 = "e115e102eb90ce160ab0ef7506b750a8d7ecc385bde0a496f02a54337a8bc333",
)
python_nuget_package(
name = "nuget_python_x86-64_3.10.0",
sha256 = "4474c83c25625d93e772e926f95f4cd398a0abbb52793625fa30f39af3d2cc00",
)

@ -0,0 +1,262 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load("@rules_python//python:defs.bzl", "py_binary")
# begin:google_only
# load("@rules_cc//cc:defs.bzl", "cc_proto_library")
# end:google_only
load(
"//bazel:upb_proto_library.bzl",
"upb_proto_library",
"upb_proto_reflection_library",
)
load(
":build_defs.bzl",
"cc_optimizefor_proto_library",
"expand_suffixes",
"proto_library",
"tmpl_cc_binary",
)
# begin:google_only
# package(default_applicable_licenses = ["//:license"])
# end:google_only
licenses(["notice"])
proto_library(
name = "descriptor_proto",
srcs = ["descriptor.proto"],
)
upb_proto_library(
name = "benchmark_descriptor_upb_proto",
deps = [":descriptor_proto"],
)
upb_proto_reflection_library(
name = "benchmark_descriptor_upb_proto_reflection",
deps = [":descriptor_proto"],
)
upb_proto_reflection_library(
name = "ads_upb_proto_reflection",
deps = ["@com_google_googleapis//:ads_proto"],
)
cc_proto_library(
name = "benchmark_descriptor_cc_proto",
deps = [":descriptor_proto"],
)
proto_library(
name = "benchmark_descriptor_sv_proto",
srcs = ["descriptor_sv.proto"],
)
cc_proto_library(
name = "benchmark_descriptor_sv_cc_proto",
deps = [":benchmark_descriptor_sv_proto"],
)
cc_test(
name = "benchmark",
testonly = 1,
srcs = ["benchmark.cc"],
deps = [
":ads_upb_proto_reflection",
":benchmark_descriptor_cc_proto",
":benchmark_descriptor_sv_cc_proto",
":benchmark_descriptor_upb_proto",
":benchmark_descriptor_upb_proto_reflection",
"//:base",
"//:base_internal",
"//:descriptor_upb_proto",
"//:mem",
"//:reflection",
"@com_github_google_benchmark//:benchmark_main",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_protobuf//:protobuf",
],
)
# Size benchmarks.
SIZE_BENCHMARKS = {
"empty": "Empty",
"descriptor": "FileDescriptorSet",
"100_msgs": "Message100",
"200_msgs": "Message200",
"100_fields": "Message",
"200_fields": "Message",
}
py_binary(
name = "gen_synthetic_protos",
srcs = ["gen_synthetic_protos.py"],
python_version = "PY3",
)
py_binary(
name = "gen_upb_binary_c",
srcs = ["gen_upb_binary_c.py"],
python_version = "PY3",
)
py_binary(
name = "gen_protobuf_binary_cc",
srcs = ["gen_protobuf_binary_cc.py"],
python_version = "PY3",
)
genrule(
name = "do_gen_synthetic_protos",
outs = [
"100_msgs.proto",
"200_msgs.proto",
"100_fields.proto",
"200_fields.proto",
],
cmd = "$(execpath :gen_synthetic_protos) $(RULEDIR)",
tools = [":gen_synthetic_protos"],
)
proto_library(
name = "100_msgs_proto",
srcs = ["100_msgs.proto"],
)
proto_library(
name = "200_msgs_proto",
srcs = ["200_msgs.proto"],
)
proto_library(
name = "100_fields_proto",
srcs = ["100_fields.proto"],
)
proto_library(
name = "200_fields_proto",
srcs = ["200_fields.proto"],
)
proto_library(
name = "empty_proto",
srcs = ["empty.proto"],
)
[(
upb_proto_library(
name = k + "_upb_proto",
deps = [":" + k + "_proto"],
),
cc_proto_library(
name = k + "_cc_proto",
deps = [":" + k + "_proto"],
),
tmpl_cc_binary(
name = k + "_upb_binary",
testonly = 1,
args = [
package_name() + "/" + k + ".upb.h",
"upb_benchmark_" + v,
],
gen = ":gen_upb_binary_c",
deps = [
":" + k + "_upb_proto",
],
),
tmpl_cc_binary(
name = k + "_protobuf_binary",
testonly = 1,
args = [
package_name() + "/" + k + ".pb.h",
"upb_benchmark::" + v,
],
gen = ":gen_protobuf_binary_cc",
deps = [
":" + k + "_cc_proto",
],
),
cc_optimizefor_proto_library(
name = k + "_cc_lite_proto",
srcs = [k + ".proto"],
outs = [k + "_lite.proto"],
optimize_for = "LITE_RUNTIME",
),
tmpl_cc_binary(
name = k + "_lite_protobuf_binary",
testonly = 1,
args = [
package_name() + "/" + k + "_lite.pb.h",
"upb_benchmark::" + v,
],
gen = ":gen_protobuf_binary_cc",
deps = [
":" + k + "_cc_lite_proto",
],
),
cc_optimizefor_proto_library(
name = k + "_cc_codesize_proto",
srcs = [k + ".proto"],
outs = [k + "_codesize.proto"],
optimize_for = "CODE_SIZE",
),
tmpl_cc_binary(
name = k + "_codesize_protobuf_binary",
testonly = 1,
args = [
package_name() + "/" + k + "_codesize.pb.h",
"upb_benchmark::" + v,
],
gen = ":gen_protobuf_binary_cc",
deps = [
":" + k + "_cc_codesize_proto",
],
),
) for k, v in SIZE_BENCHMARKS.items()]
genrule(
name = "size_data",
testonly = 1,
srcs = expand_suffixes(
SIZE_BENCHMARKS.keys(),
suffixes = [
"_upb_binary",
"_protobuf_binary",
"_lite_protobuf_binary",
"_codesize_protobuf_binary",
],
),
outs = ["size_data.txt"],
# We want --format=GNU which counts rodata with data, not text.
cmd = "size $$($$OSTYPE == 'linux-gnu' ? '--format=GNU -d' : '') $(SRCS) > $@",
# "size" sometimes isn't available remotely.
local = 1,
tags = ["no-remote-exec"],
)

@ -0,0 +1,59 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load(
"@rules_proto//proto:defs.bzl",
"proto_library",
)
proto_library(
name = "ads_proto",
srcs = glob([
"google/ads/googleads/v13/**/*.proto",
"google/api/**/*.proto",
"google/rpc/**/*.proto",
"google/longrunning/**/*.proto",
"google/logging/**/*.proto",
]),
#srcs = ["google/ads/googleads/v5/services/google_ads_service.proto"],
visibility = ["//visibility:public"],
deps = [
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:api_proto",
"@com_google_protobuf//:descriptor_proto",
"@com_google_protobuf//:duration_proto",
"@com_google_protobuf//:empty_proto",
"@com_google_protobuf//:field_mask_proto",
"@com_google_protobuf//:struct_proto",
"@com_google_protobuf//:timestamp_proto",
"@com_google_protobuf//:type_proto",
"@com_google_protobuf//:wrappers_proto",
],
)

@ -0,0 +1,390 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <benchmark/benchmark.h>
#include <string.h>
#include <vector>
#include "google/ads/googleads/v13/services/google_ads_service.upbdefs.h"
#include "google/protobuf/descriptor.pb.h"
#include "absl/container/flat_hash_set.h"
#include "google/protobuf/dynamic_message.h"
#include "benchmarks/descriptor.pb.h"
#include "benchmarks/descriptor.upb.h"
#include "benchmarks/descriptor.upbdefs.h"
#include "benchmarks/descriptor_sv.pb.h"
#include "upb/base/internal/log2.h"
#include "upb/mem/arena.h"
#include "upb/reflection/def.hpp"
upb_StringView descriptor = benchmarks_descriptor_proto_upbdefinit.descriptor;
namespace protobuf = ::google::protobuf;
// A buffer big enough to parse descriptor.proto without going to heap.
// We use 64-bit ints here to force alignment.
int64_t buf[8191];
void CollectFileDescriptors(
const _upb_DefPool_Init* file,
std::vector<upb_StringView>& serialized_files,
absl::flat_hash_set<const _upb_DefPool_Init*>& seen) {
if (!seen.insert(file).second) return;
for (_upb_DefPool_Init** deps = file->deps; *deps; deps++) {
CollectFileDescriptors(*deps, serialized_files, seen);
}
serialized_files.push_back(file->descriptor);
}
static void BM_ArenaOneAlloc(benchmark::State& state) {
for (auto _ : state) {
upb_Arena* arena = upb_Arena_New();
upb_Arena_Malloc(arena, 1);
upb_Arena_Free(arena);
}
}
BENCHMARK(BM_ArenaOneAlloc);
static void BM_ArenaInitialBlockOneAlloc(benchmark::State& state) {
for (auto _ : state) {
upb_Arena* arena = upb_Arena_Init(buf, sizeof(buf), nullptr);
upb_Arena_Malloc(arena, 1);
upb_Arena_Free(arena);
}
}
BENCHMARK(BM_ArenaInitialBlockOneAlloc);
static void BM_ArenaFuseUnbalanced(benchmark::State& state) {
std::vector<upb_Arena*> arenas(state.range(0));
size_t n = 0;
for (auto _ : state) {
for (auto& arena : arenas) {
arena = upb_Arena_New();
}
for (auto& arena : arenas) {
upb_Arena_Fuse(arenas[0], arena);
}
for (auto& arena : arenas) {
upb_Arena_Free(arena);
}
n += arenas.size();
}
state.SetItemsProcessed(n);
}
BENCHMARK(BM_ArenaFuseUnbalanced)->Range(2, 128);
static void BM_ArenaFuseBalanced(benchmark::State& state) {
std::vector<upb_Arena*> arenas(state.range(0));
size_t n = 0;
for (auto _ : state) {
for (auto& arena : arenas) {
arena = upb_Arena_New();
}
// Perform a series of fuses that keeps the halves balanced.
size_t max = upb_Log2Ceiling(arenas.size());
for (size_t n = 0; n <= max; n++) {
size_t step = 1 << n;
for (size_t i = 0; i + step < arenas.size(); i += (step * 2)) {
upb_Arena_Fuse(arenas[i], arenas[i + step]);
}
}
for (auto& arena : arenas) {
upb_Arena_Free(arena);
}
n += arenas.size();
}
state.SetItemsProcessed(n);
}
BENCHMARK(BM_ArenaFuseBalanced)->Range(2, 128);
enum LoadDescriptorMode {
NoLayout,
WithLayout,
};
// This function is mostly copied from upb/def.c, but it is modified to avoid
// passing in the pre-generated mini-tables, in order to force upb to compute
// them dynamically. Generally you would never want to do this, but we want to
// simulate the cost we would pay if we were loading these types purely from
// descriptors, with no mini-tales available.
bool LoadDefInit_BuildLayout(upb_DefPool* s, const _upb_DefPool_Init* init,
size_t* bytes) {
_upb_DefPool_Init** deps = init->deps;
google_protobuf_FileDescriptorProto* file;
upb_Arena* arena;
upb_Status status;
upb_Status_Clear(&status);
if (upb_DefPool_FindFileByName(s, init->filename)) {
return true;
}
arena = upb_Arena_New();
for (; *deps; deps++) {
if (!LoadDefInit_BuildLayout(s, *deps, bytes)) goto err;
}
file = google_protobuf_FileDescriptorProto_parse_ex(
init->descriptor.data, init->descriptor.size, nullptr,
kUpb_DecodeOption_AliasString, arena);
*bytes += init->descriptor.size;
if (!file) {
upb_Status_SetErrorFormat(
&status,
"Failed to parse compiled-in descriptor for file '%s'. This should "
"never happen.",
init->filename);
goto err;
}
// KEY DIFFERENCE: Here we pass in only the descriptor, and not the
// pre-generated minitables.
if (!upb_DefPool_AddFile(s, file, &status)) {
goto err;
}
upb_Arena_Free(arena);
return true;
err:
fprintf(stderr,
"Error loading compiled-in descriptor for file '%s' (this should "
"never happen): %s\n",
init->filename, upb_Status_ErrorMessage(&status));
exit(1);
}
template <LoadDescriptorMode Mode>
static void BM_LoadAdsDescriptor_Upb(benchmark::State& state) {
size_t bytes_per_iter = 0;
for (auto _ : state) {
upb::DefPool defpool;
if (Mode == NoLayout) {
google_ads_googleads_v13_services_SearchGoogleAdsRequest_getmsgdef(
defpool.ptr());
bytes_per_iter = _upb_DefPool_BytesLoaded(defpool.ptr());
} else {
bytes_per_iter = 0;
LoadDefInit_BuildLayout(
defpool.ptr(),
&google_ads_googleads_v13_services_google_ads_service_proto_upbdefinit,
&bytes_per_iter);
}
}
state.SetBytesProcessed(state.iterations() * bytes_per_iter);
}
BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Upb, NoLayout);
BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Upb, WithLayout);
template <LoadDescriptorMode Mode>
static void BM_LoadAdsDescriptor_Proto2(benchmark::State& state) {
extern _upb_DefPool_Init
google_ads_googleads_v13_services_google_ads_service_proto_upbdefinit;
std::vector<upb_StringView> serialized_files;
absl::flat_hash_set<const _upb_DefPool_Init*> seen_files;
CollectFileDescriptors(
&google_ads_googleads_v13_services_google_ads_service_proto_upbdefinit,
serialized_files, seen_files);
size_t bytes_per_iter = 0;
for (auto _ : state) {
bytes_per_iter = 0;
protobuf::Arena arena;
protobuf::DescriptorPool pool;
for (auto file : serialized_files) {
absl::string_view input(file.data, file.size);
auto proto =
protobuf::Arena::CreateMessage<protobuf::FileDescriptorProto>(&arena);
bool ok = proto->ParseFrom<protobuf::MessageLite::kMergePartial>(input) &&
pool.BuildFile(*proto) != nullptr;
if (!ok) {
printf("Failed to add file.\n");
exit(1);
}
bytes_per_iter += input.size();
}
if (Mode == WithLayout) {
protobuf::DynamicMessageFactory factory;
const protobuf::Descriptor* d = pool.FindMessageTypeByName(
"google.ads.googleads.v13.services.SearchGoogleAdsResponse");
if (!d) {
printf("Failed to find descriptor.\n");
exit(1);
}
factory.GetPrototype(d);
}
}
state.SetBytesProcessed(state.iterations() * bytes_per_iter);
}
BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Proto2, NoLayout);
BENCHMARK_TEMPLATE(BM_LoadAdsDescriptor_Proto2, WithLayout);
enum CopyStrings {
Copy,
Alias,
};
enum ArenaMode {
NoArena,
UseArena,
InitBlock,
};
template <ArenaMode AMode, CopyStrings Copy>
static void BM_Parse_Upb_FileDesc(benchmark::State& state) {
for (auto _ : state) {
upb_Arena* arena;
if (AMode == InitBlock) {
arena = upb_Arena_Init(buf, sizeof(buf), nullptr);
} else {
arena = upb_Arena_New();
}
upb_benchmark_FileDescriptorProto* set =
upb_benchmark_FileDescriptorProto_parse_ex(
descriptor.data, descriptor.size, nullptr,
Copy == Alias ? kUpb_DecodeOption_AliasString : 0, arena);
if (!set) {
printf("Failed to parse.\n");
exit(1);
}
upb_Arena_Free(arena);
}
state.SetBytesProcessed(state.iterations() * descriptor.size);
}
BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, UseArena, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, UseArena, Alias);
BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, InitBlock, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Upb_FileDesc, InitBlock, Alias);
template <ArenaMode AMode, class P>
struct Proto2Factory;
template <class P>
struct Proto2Factory<NoArena, P> {
public:
P* GetProto() { return &proto; }
private:
P proto;
};
template <class P>
struct Proto2Factory<UseArena, P> {
public:
P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena); }
private:
protobuf::Arena arena;
};
template <class P>
struct Proto2Factory<InitBlock, P> {
public:
Proto2Factory() : arena(GetOptions()) {}
P* GetProto() { return protobuf::Arena::CreateMessage<P>(&arena); }
private:
protobuf::ArenaOptions GetOptions() {
protobuf::ArenaOptions opts;
opts.initial_block = (char*)buf;
opts.initial_block_size = sizeof(buf);
return opts;
}
protobuf::Arena arena;
};
using FileDesc = ::upb_benchmark::FileDescriptorProto;
using FileDescSV = ::upb_benchmark::sv::FileDescriptorProto;
template <class P, ArenaMode AMode, CopyStrings kCopy>
void BM_Parse_Proto2(benchmark::State& state) {
constexpr protobuf::MessageLite::ParseFlags kParseFlags =
kCopy == Copy
? protobuf::MessageLite::ParseFlags::kMergePartial
: protobuf::MessageLite::ParseFlags::kMergePartialWithAliasing;
for (auto _ : state) {
Proto2Factory<AMode, P> proto_factory;
auto proto = proto_factory.GetProto();
absl::string_view input(descriptor.data, descriptor.size);
bool ok = proto->template ParseFrom<kParseFlags>(input);
if (!ok) {
printf("Failed to parse.\n");
exit(1);
}
}
state.SetBytesProcessed(state.iterations() * descriptor.size);
}
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, NoArena, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, UseArena, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDesc, InitBlock, Copy);
BENCHMARK_TEMPLATE(BM_Parse_Proto2, FileDescSV, InitBlock, Alias);
static void BM_SerializeDescriptor_Proto2(benchmark::State& state) {
upb_benchmark::FileDescriptorProto proto;
proto.ParseFromArray(descriptor.data, descriptor.size);
for (auto _ : state) {
proto.SerializePartialToArray(buf, sizeof(buf));
}
state.SetBytesProcessed(state.iterations() * descriptor.size);
}
BENCHMARK(BM_SerializeDescriptor_Proto2);
static void BM_SerializeDescriptor_Upb(benchmark::State& state) {
int64_t total = 0;
upb_Arena* arena = upb_Arena_New();
upb_benchmark_FileDescriptorProto* set =
upb_benchmark_FileDescriptorProto_parse(descriptor.data, descriptor.size,
arena);
if (!set) {
printf("Failed to parse.\n");
exit(1);
}
for (auto _ : state) {
upb_Arena* enc_arena = upb_Arena_Init(buf, sizeof(buf), nullptr);
size_t size;
char* data =
upb_benchmark_FileDescriptorProto_serialize(set, enc_arena, &size);
if (!data) {
printf("Failed to serialize.\n");
exit(1);
}
total += size;
}
state.SetBytesProcessed(total);
}
BENCHMARK(BM_SerializeDescriptor_Upb);

@ -0,0 +1,88 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# begin:google_only
# load("@rules_cc//cc:defs.bzl", _cc_proto_library = "cc_proto_library")
#
# _is_google3 = True
# end:google_only
# begin:github_only
_cc_proto_library = native.cc_proto_library
_is_google3 = False
# end:github_only
def proto_library(**kwargs):
if _is_google3:
kwargs["cc_api_version"] = 2
native.proto_library(
**kwargs
)
def tmpl_cc_binary(name, gen, args, replacements = [], **kwargs):
srcs = [name + ".cc"]
native.genrule(
name = name + "_gen_srcs",
tools = [gen],
outs = srcs,
cmd = "$(location " + gen + ") " + " ".join(args) + " > $@",
)
if _is_google3:
kwargs["malloc"] = "//base:system_malloc"
kwargs["features"] = ["-static_linking_mode"]
native.cc_binary(
name = name,
srcs = srcs,
**kwargs
)
def cc_optimizefor_proto_library(name, srcs, outs, optimize_for):
if len(srcs) != 1:
fail("Currently srcs must have exactly 1 element")
native.genrule(
name = name + "_gen_proto",
srcs = srcs,
outs = outs,
cmd = "cp $< $@ && chmod a+w $@ && echo 'option optimize_for = " + optimize_for + ";' >> $@",
)
proto_library(
name = name + "_proto",
srcs = outs,
)
_cc_proto_library(
name = name,
deps = [":" + name + "_proto"],
)
def expand_suffixes(vals, suffixes):
ret = []
for val in vals:
for suffix in suffixes:
ret.append(val + suffix)
return ret

@ -0,0 +1,123 @@
#!/usr/bin/python3
#
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Benchmarks the current working directory against a given baseline.
This script benchmarks both size and speed. Sample output:
"""
import contextlib
import json
import os
import re
import subprocess
import sys
import tempfile
@contextlib.contextmanager
def GitWorktree(commit):
tmpdir = tempfile.mkdtemp()
subprocess.run(['git', 'worktree', 'add', '-q', '-d', tmpdir, commit], check=True)
cwd = os.getcwd()
os.chdir(tmpdir)
try:
yield tmpdir
finally:
os.chdir(cwd)
subprocess.run(['git', 'worktree', 'remove', tmpdir], check=True)
def Run(cmd):
subprocess.check_call(cmd, shell=True)
def Benchmark(outbase, bench_cpu=True, runs=12, fasttable=False):
tmpfile = "/tmp/bench-output.json"
Run("rm -rf {}".format(tmpfile))
#Run("CC=clang bazel test ...")
if fasttable:
extra_args = " --//:fasttable_enabled=true"
else:
extra_args = ""
if bench_cpu:
Run("CC=clang bazel build -c opt --copt=-march=native benchmarks:benchmark" + extra_args)
Run("./bazel-bin/benchmarks/benchmark --benchmark_out_format=json --benchmark_out={} --benchmark_repetitions={} --benchmark_min_time=0.05 --benchmark_enable_random_interleaving=true".format(tmpfile, runs))
with open(tmpfile) as f:
bench_json = json.load(f)
# Translate into the format expected by benchstat.
txt_filename = outbase + ".txt"
with open(txt_filename, "w") as f:
for run in bench_json["benchmarks"]:
if run["run_type"] == "aggregate":
continue
name = run["name"]
name = name.replace(" ", "")
name = re.sub(r'^BM_', 'Benchmark', name)
values = (name, run["iterations"], run["cpu_time"])
print("{} {} {} ns/op".format(*values), file=f)
Run("sort {} -o {} ".format(txt_filename, txt_filename))
Run("CC=clang bazel build -c opt --copt=-g --copt=-march=native :conformance_upb"
+ extra_args)
Run("cp -f bazel-bin/conformance_upb {}.bin".format(outbase))
baseline = "main"
bench_cpu = True
fasttable = False
if len(sys.argv) > 1:
baseline = sys.argv[1]
# Quickly verify that the baseline exists.
with GitWorktree(baseline):
pass
# Benchmark our current directory first, since it's more likely to be broken.
Benchmark("/tmp/new", bench_cpu, fasttable=fasttable)
# Benchmark the baseline.
with GitWorktree(baseline):
Benchmark("/tmp/old", bench_cpu, fasttable=fasttable)
print()
print()
if bench_cpu:
Run("~/go/bin/benchstat /tmp/old.txt /tmp/new.txt")
print()
print()
Run("objcopy --strip-debug /tmp/old.bin /tmp/old.bin.stripped")
Run("objcopy --strip-debug /tmp/new.bin /tmp/new.bin.stripped")
Run("~/code/bloaty/bloaty /tmp/new.bin.stripped -- /tmp/old.bin.stripped --debug-file=/tmp/old.bin --debug-file=/tmp/new.bin -d compileunits,symbols")

@ -0,0 +1,888 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// The messages in this file describe the definitions found in .proto files.
// A valid .proto file can be translated directly to a FileDescriptorProto
// without any other information (e.g. without reading its imports).
syntax = "proto2";
package upb_benchmark;
option go_package = "google.golang.org/protobuf/types/descriptorpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
option csharp_namespace = "Google.Protobuf.Reflection";
option objc_class_prefix = "UPBB";
option cc_enable_arenas = true;
// The protocol compiler can output a FileDescriptorSet containing the .proto
// files it parses.
message FileDescriptorSet {
repeated FileDescriptorProto file = 1;
}
// Describes a complete .proto file.
message FileDescriptorProto {
optional string name = 1; // file name, relative to root of source tree
optional string package = 2; // e.g. "foo", "foo.bar", etc.
// Names of files imported by this file.
repeated string dependency = 3;
// Indexes of the public imported files in the dependency list above.
repeated int32 public_dependency = 10;
// Indexes of the weak imported files in the dependency list.
// For Google-internal migration only. Do not use.
repeated int32 weak_dependency = 11;
// All top-level definitions in this file.
repeated DescriptorProto message_type = 4;
repeated EnumDescriptorProto enum_type = 5;
repeated ServiceDescriptorProto service = 6;
repeated FieldDescriptorProto extension = 7;
optional FileOptions options = 8;
// This field contains optional information about the original source code.
// You may safely remove this entire field without harming runtime
// functionality of the descriptors -- the information is needed only by
// development tools.
optional SourceCodeInfo source_code_info = 9;
// The syntax of the proto file.
// The supported values are "proto2" and "proto3".
optional string syntax = 12;
}
// Describes a message type.
message DescriptorProto {
optional string name = 1;
repeated FieldDescriptorProto field = 2;
repeated FieldDescriptorProto extension = 6;
repeated DescriptorProto nested_type = 3;
repeated EnumDescriptorProto enum_type = 4;
message ExtensionRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
optional ExtensionRangeOptions options = 3;
}
repeated ExtensionRange extension_range = 5;
repeated OneofDescriptorProto oneof_decl = 8;
optional MessageOptions options = 7;
// Range of reserved tag numbers. Reserved tag numbers may not be used by
// fields or extension ranges in the same message. Reserved ranges may
// not overlap.
message ReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
}
repeated ReservedRange reserved_range = 9;
// Reserved field names, which may not be used by fields in the same message.
// A given name may only be reserved once.
repeated string reserved_name = 10;
}
message ExtensionRangeOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// Describes a field within a message.
message FieldDescriptorProto {
enum Type {
// 0 is reserved for errors.
// Order is weird for historical reasons.
TYPE_DOUBLE = 1;
TYPE_FLOAT = 2;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
// negative values are likely.
TYPE_INT64 = 3;
TYPE_UINT64 = 4;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
// negative values are likely.
TYPE_INT32 = 5;
TYPE_FIXED64 = 6;
TYPE_FIXED32 = 7;
TYPE_BOOL = 8;
TYPE_STRING = 9;
// Tag-delimited aggregate.
// Group type is deprecated and not supported in proto3. However, Proto3
// implementations should still be able to parse the group wire format and
// treat group fields as unknown fields.
TYPE_GROUP = 10;
TYPE_MESSAGE = 11; // Length-delimited aggregate.
// New in version 2.
TYPE_BYTES = 12;
TYPE_UINT32 = 13;
TYPE_ENUM = 14;
TYPE_SFIXED32 = 15;
TYPE_SFIXED64 = 16;
TYPE_SINT32 = 17; // Uses ZigZag encoding.
TYPE_SINT64 = 18; // Uses ZigZag encoding.
}
enum Label {
// 0 is reserved for errors
LABEL_OPTIONAL = 1;
LABEL_REQUIRED = 2;
LABEL_REPEATED = 3;
}
optional string name = 1;
optional int32 number = 3;
optional Label label = 4;
// If type_name is set, this need not be set. If both this and type_name
// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
optional Type type = 5;
// For message and enum types, this is the name of the type. If the name
// starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
// rules are used to find the type (i.e. first the nested types within this
// message are searched, then within the parent, on up to the root
// namespace).
optional string type_name = 6;
// For extensions, this is the name of the type being extended. It is
// resolved in the same manner as type_name.
optional string extendee = 2;
// For numeric types, contains the original text representation of the value.
// For booleans, "true" or "false".
// For strings, contains the default text contents (not escaped in any way).
// For bytes, contains the C escaped value. All bytes >= 128 are escaped.
// TODO(kenton): Base-64 encode?
optional string default_value = 7;
// If set, gives the index of a oneof in the containing type's oneof_decl
// list. This field is a member of that oneof.
optional int32 oneof_index = 9;
// JSON name of this field. The value is set by protocol compiler. If the
// user has set a "json_name" option on this field, that option's value
// will be used. Otherwise, it's deduced from the field's name by converting
// it to camelCase.
optional string json_name = 10;
optional FieldOptions options = 8;
// If true, this is a proto3 "optional". When a proto3 field is optional, it
// tracks presence regardless of field type.
//
// When proto3_optional is true, this field must be belong to a oneof to
// signal to old proto3 clients that presence is tracked for this field. This
// oneof is known as a "synthetic" oneof, and this field must be its sole
// member (each proto3 optional field gets its own synthetic oneof). Synthetic
// oneofs exist in the descriptor only, and do not generate any API. Synthetic
// oneofs must be ordered after all "real" oneofs.
//
// For message fields, proto3_optional doesn't create any semantic change,
// since non-repeated message fields always track presence. However it still
// indicates the semantic detail of whether the user wrote "optional" or not.
// This can be useful for round-tripping the .proto file. For consistency we
// give message fields a synthetic oneof also, even though it is not required
// to track presence. This is especially important because the parser can't
// tell if a field is a message or an enum, so it must always create a
// synthetic oneof.
//
// Proto2 optional fields do not set this flag, because they already indicate
// optional with `LABEL_OPTIONAL`.
optional bool proto3_optional = 17;
}
// Describes a oneof.
message OneofDescriptorProto {
optional string name = 1;
optional OneofOptions options = 2;
}
// Describes an enum type.
message EnumDescriptorProto {
optional string name = 1;
repeated EnumValueDescriptorProto value = 2;
optional EnumOptions options = 3;
// Range of reserved numeric values. Reserved values may not be used by
// entries in the same enum. Reserved ranges may not overlap.
//
// Note that this is distinct from DescriptorProto.ReservedRange in that it
// is inclusive such that it can appropriately represent the entire int32
// domain.
message EnumReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Inclusive.
}
// Range of reserved numeric values. Reserved numeric values may not be used
// by enum values in the same enum declaration. Reserved ranges may not
// overlap.
repeated EnumReservedRange reserved_range = 4;
// Reserved enum value names, which may not be reused. A given name may only
// be reserved once.
repeated string reserved_name = 5;
}
// Describes a value within an enum.
message EnumValueDescriptorProto {
optional string name = 1;
optional int32 number = 2;
optional EnumValueOptions options = 3;
}
// Describes a service.
message ServiceDescriptorProto {
optional string name = 1;
repeated MethodDescriptorProto method = 2;
optional ServiceOptions options = 3;
}
// Describes a method of a service.
message MethodDescriptorProto {
optional string name = 1;
// Input and output type names. These are resolved in the same way as
// FieldDescriptorProto.type_name, but must refer to a message type.
optional string input_type = 2;
optional string output_type = 3;
optional MethodOptions options = 4;
// Identifies if client streams multiple client messages
optional bool client_streaming = 5 [default = false];
// Identifies if server streams multiple server messages
optional bool server_streaming = 6 [default = false];
}
// ===================================================================
// Options
// Each of the definitions above may have "options" attached. These are
// just annotations which may cause code to be generated slightly differently
// or may contain hints for code that manipulates protocol messages.
//
// Clients may define custom options as extensions of the *Options messages.
// These extensions may not yet be known at parsing time, so the parser cannot
// store the values in them. Instead it stores them in a field in the *Options
// message called uninterpreted_option. This field must have the same name
// across all *Options messages. We then use this field to populate the
// extensions when we build a descriptor, at which point all protos have been
// parsed and so all extensions are known.
//
// Extension numbers for custom options may be chosen as follows:
// * For options which will only be used within a single application or
// organization, or for experimental options, use field numbers 50000
// through 99999. It is up to you to ensure that you do not use the
// same number for multiple options.
// * For options which will be published and used publicly by multiple
// independent entities, e-mail protobuf-global-extension-registry@google.com
// to reserve extension numbers. Simply provide your project name (e.g.
// Objective-C plugin) and your project website (if available) -- there's no
// need to explain how you intend to use them. Usually you only need one
// extension number. You can declare multiple options with only one extension
// number by putting them in a sub-message. See the Custom Options section of
// the docs for examples:
// https://developers.google.com/protocol-buffers/docs/proto#options
// If this turns out to be popular, a web service will be set up
// to automatically assign option numbers.
message FileOptions {
// Sets the Java package where classes generated from this .proto will be
// placed. By default, the proto package is used, but this is often
// inappropriate because proto packages do not normally start with backwards
// domain names.
optional string java_package = 1;
// If set, all the classes from the .proto file are wrapped in a single
// outer class with the given name. This applies to both Proto1
// (equivalent to the old "--one_java_file" option) and Proto2 (where
// a .proto always translates to a single class, but you may want to
// explicitly choose the class name).
optional string java_outer_classname = 8;
// If set true, then the Java code generator will generate a separate .java
// file for each top-level message, enum, and service defined in the .proto
// file. Thus, these types will *not* be nested inside the outer class
// named by java_outer_classname. However, the outer class will still be
// generated to contain the file's getDescriptor() method as well as any
// top-level extensions defined in the file.
optional bool java_multiple_files = 10 [default = false];
// This option does nothing.
optional bool java_generate_equals_and_hash = 20 [deprecated = true];
// If set true, then the Java2 code generator will generate code that
// throws an exception whenever an attempt is made to assign a non-UTF-8
// byte sequence to a string field.
// Message reflection will do the same.
// However, an extension field still accepts non-UTF-8 byte sequences.
// This option has no effect on when used with the lite runtime.
optional bool java_string_check_utf8 = 27 [default = false];
// Generated classes can be optimized for speed or code size.
enum OptimizeMode {
SPEED = 1; // Generate complete code for parsing, serialization,
// etc.
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
}
optional OptimizeMode optimize_for = 9 [default = SPEED];
// Sets the Go package where structs generated from this .proto will be
// placed. If omitted, the Go package will be derived from the following:
// - The basename of the package import path, if provided.
// - Otherwise, the package statement in the .proto file, if present.
// - Otherwise, the basename of the .proto file, without extension.
optional string go_package = 11;
// Should generic services be generated in each language? "Generic" services
// are not specific to any particular RPC system. They are generated by the
// main code generators in each language (without additional plugins).
// Generic services were the only kind of service generation supported by
// early versions of google.protobuf.
//
// Generic services are now considered deprecated in favor of using plugins
// that generate code specific to your particular RPC system. Therefore,
// these default to false. Old code which depends on generic services should
// explicitly set them to true.
optional bool cc_generic_services = 16 [default = false];
optional bool java_generic_services = 17 [default = false];
optional bool py_generic_services = 18 [default = false];
optional bool php_generic_services = 42 [default = false];
// Is this file deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for everything in the file, or it will be completely ignored; in the very
// least, this is a formalization for deprecating files.
optional bool deprecated = 23 [default = false];
// Enables the use of arenas for the proto messages in this file. This applies
// only to generated classes for C++.
optional bool cc_enable_arenas = 31 [default = true];
// Sets the objective c class prefix which is prepended to all objective c
// generated classes from this .proto. There is no default.
optional string objc_class_prefix = 36;
// Namespace for generated classes; defaults to the package.
optional string csharp_namespace = 37;
// By default Swift generators will take the proto package and CamelCase it
// replacing '.' with underscore and use that to prefix the types/symbols
// defined. When this options is provided, they will use this value instead
// to prefix the types/symbols defined.
optional string swift_prefix = 39;
// Sets the php class prefix which is prepended to all php generated classes
// from this .proto. Default is empty.
optional string php_class_prefix = 40;
// Use this option to change the namespace of php generated classes. Default
// is empty. When this option is empty, the package name will be used for
// determining the namespace.
optional string php_namespace = 41;
// Use this option to change the namespace of php generated metadata classes.
// Default is empty. When this option is empty, the proto file name will be
// used for determining the namespace.
optional string php_metadata_namespace = 44;
// Use this option to change the package of ruby generated classes. Default
// is empty. When this option is not set, the package name will be used for
// determining the ruby package.
optional string ruby_package = 45;
// The parser stores options it doesn't recognize here.
// See the documentation for the "Options" section above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message.
// See the documentation for the "Options" section above.
extensions 1000 to max;
reserved 38;
}
message MessageOptions {
// Set true to use the old proto1 MessageSet wire format for extensions.
// This is provided for backwards-compatibility with the MessageSet wire
// format. You should not use this for any other reason: It's less
// efficient, has fewer features, and is more complicated.
//
// The message must be defined exactly as follows:
// message Foo {
// option message_set_wire_format = true;
// extensions 4 to max;
// }
// Note that the message cannot have any defined fields; MessageSets only
// have extensions.
//
// All extensions of your type must be singular messages; e.g. they cannot
// be int32s, enums, or repeated messages.
//
// Because this is an option, the above two restrictions are not enforced by
// the protocol compiler.
optional bool message_set_wire_format = 1 [default = false];
// Disables the generation of the standard "descriptor()" accessor, which can
// conflict with a field of the same name. This is meant to make migration
// from proto1 easier; new code should avoid fields named "descriptor".
optional bool no_standard_descriptor_accessor = 2 [default = false];
// Is this message deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the message, or it will be completely ignored; in the very least,
// this is a formalization for deprecating messages.
optional bool deprecated = 3 [default = false];
// Whether the message is an automatically generated map entry type for the
// maps field.
//
// For maps fields:
// map<KeyType, ValueType> map_field = 1;
// The parsed descriptor looks like:
// message MapFieldEntry {
// option map_entry = true;
// optional KeyType key = 1;
// optional ValueType value = 2;
// }
// repeated MapFieldEntry map_field = 1;
//
// Implementations may choose not to generate the map_entry=true message, but
// use a native map in the target language to hold the keys and values.
// The reflection APIs in such implementations still need to work as
// if the field is a repeated message field.
//
// NOTE: Do not set the option in .proto files. Always use the maps syntax
// instead. The option should only be implicitly set by the proto compiler
// parser.
optional bool map_entry = 7;
reserved 8; // javalite_serializable
reserved 9; // javanano_as_lite
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message FieldOptions {
// The ctype option instructs the C++ code generator to use a different
// representation of the field than it normally would. See the specific
// options below. This option is not yet implemented in the open source
// release -- sorry, we'll try to include it in a future version!
optional CType ctype = 1 [default = STRING];
enum CType {
// Default mode.
STRING = 0;
CORD = 1;
STRING_PIECE = 2;
}
// The packed option can be enabled for repeated primitive fields to enable
// a more efficient representation on the wire. Rather than repeatedly
// writing the tag and type for each element, the entire array is encoded as
// a single length-delimited blob. In proto3, only explicit setting it to
// false will avoid using packed encoding.
optional bool packed = 2;
// The jstype option determines the JavaScript type used for values of the
// field. The option is permitted only for 64 bit integral and fixed types
// (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
// is represented as JavaScript string, which avoids loss of precision that
// can happen when a large value is converted to a floating point JavaScript.
// Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
// use the JavaScript "number" type. The behavior of the default option
// JS_NORMAL is implementation dependent.
//
// This option is an enum to permit additional types to be added, e.g.
// goog.math.Integer.
optional JSType jstype = 6 [default = JS_NORMAL];
enum JSType {
// Use the default type.
JS_NORMAL = 0;
// Use JavaScript strings.
JS_STRING = 1;
// Use JavaScript numbers.
JS_NUMBER = 2;
}
// Should this field be parsed lazily? Lazy applies only to message-type
// fields. It means that when the outer message is initially parsed, the
// inner message's contents will not be parsed but instead stored in encoded
// form. The inner message will actually be parsed when it is first accessed.
//
// This is only a hint. Implementations are free to choose whether to use
// eager or lazy parsing regardless of the value of this option. However,
// setting this option true suggests that the protocol author believes that
// using lazy parsing on this field is worth the additional bookkeeping
// overhead typically needed to implement it.
//
// This option does not affect the public interface of any generated code;
// all method signatures remain the same. Furthermore, thread-safety of the
// interface is not affected by this option; const methods remain safe to
// call from multiple threads concurrently, while non-const methods continue
// to require exclusive access.
//
//
// Note that implementations may choose not to check required fields within
// a lazy sub-message. That is, calling IsInitialized() on the outer message
// may return true even if the inner message has missing required fields.
// This is necessary because otherwise the inner message would have to be
// parsed in order to perform the check, defeating the purpose of lazy
// parsing. An implementation which chooses not to check required fields
// must be consistent about it. That is, for any particular sub-message, the
// implementation must either *always* check its required fields, or *never*
// check its required fields, regardless of whether or not the message has
// been parsed.
optional bool lazy = 5 [default = false];
// Is this field deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for accessors, or it will be completely ignored; in the very least, this
// is a formalization for deprecating fields.
optional bool deprecated = 3 [default = false];
// For Google-internal migration only. Do not use.
optional bool weak = 10 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
reserved 4; // removed jtype
}
message OneofOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumOptions {
// Set this option to true to allow mapping different tag names to the same
// value.
optional bool allow_alias = 2;
// Is this enum deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum, or it will be completely ignored; in the very least, this
// is a formalization for deprecating enums.
optional bool deprecated = 3 [default = false];
reserved 5; // javanano_as_lite
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumValueOptions {
// Is this enum value deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum value, or it will be completely ignored; in the very least,
// this is a formalization for deprecating enum values.
optional bool deprecated = 1 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message ServiceOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this service deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the service, or it will be completely ignored; in the very least,
// this is a formalization for deprecating services.
optional bool deprecated = 33 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message MethodOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this method deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the method, or it will be completely ignored; in the very least,
// this is a formalization for deprecating methods.
optional bool deprecated = 33 [default = false];
// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
// or neither? HTTP based RPC implementation may choose GET verb for safe
// methods, and PUT verb for idempotent methods instead of the default POST.
enum IdempotencyLevel {
IDEMPOTENCY_UNKNOWN = 0;
NO_SIDE_EFFECTS = 1; // implies idempotent
IDEMPOTENT = 2; // idempotent, but may have side effects
}
optional IdempotencyLevel idempotency_level = 34
[default = IDEMPOTENCY_UNKNOWN];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// A message representing a option the parser does not recognize. This only
// appears in options protos created by the compiler::Parser class.
// DescriptorPool resolves these when building Descriptor objects. Therefore,
// options protos in descriptor objects (e.g. returned by Descriptor::options(),
// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
// in them.
message UninterpretedOption {
// The name of the uninterpreted option. Each string represents a segment in
// a dot-separated name. is_extension is true iff a segment represents an
// extension (denoted with parentheses in options specs in .proto files).
// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
// "foo.(bar.baz).qux".
message NamePart {
optional string name_part = 1;
optional bool is_extension = 2;
}
repeated NamePart name = 2;
// The value of the uninterpreted option, in whatever type the tokenizer
// identified it as during parsing. Exactly one of these should be set.
optional string identifier_value = 3;
optional uint64 positive_int_value = 4;
optional int64 negative_int_value = 5;
optional double double_value = 6;
optional bytes string_value = 7;
optional string aggregate_value = 8;
}
// ===================================================================
// Optional source code info
// Encapsulates information about the original source file from which a
// FileDescriptorProto was generated.
message SourceCodeInfo {
// A Location identifies a piece of source code in a .proto file which
// corresponds to a particular definition. This information is intended
// to be useful to IDEs, code indexers, documentation generators, and similar
// tools.
//
// For example, say we have a file like:
// message Foo {
// optional string foo = 1;
// }
// Let's look at just the field definition:
// optional string foo = 1;
// ^ ^^ ^^ ^ ^^^
// a bc de f ghi
// We have the following locations:
// span path represents
// [a,i) [ 4, 0, 2, 0 ] The whole field definition.
// [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
// [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
// [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
// [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
//
// Notes:
// - A location may refer to a repeated field itself (i.e. not to any
// particular index within it). This is used whenever a set of elements are
// logically enclosed in a single code segment. For example, an entire
// extend block (possibly containing multiple extension definitions) will
// have an outer location whose path refers to the "extensions" repeated
// field without an index.
// - Multiple locations may have the same path. This happens when a single
// logical declaration is spread out across multiple places. The most
// obvious example is the "extend" block again -- there may be multiple
// extend blocks in the same scope, each of which will have the same path.
// - A location's span is not always a subset of its parent's span. For
// example, the "extendee" of an extension declaration appears at the
// beginning of the "extend" block and is shared by all extensions within
// the block.
// - Just because a location's span is a subset of some other location's span
// does not mean that it is a descendant. For example, a "group" defines
// both a type and a field in a single declaration. Thus, the locations
// corresponding to the type and field and their components will overlap.
// - Code which tries to interpret locations should probably be designed to
// ignore those that it doesn't understand, as more types of locations could
// be recorded in the future.
repeated Location location = 1;
message Location {
// Identifies which part of the FileDescriptorProto was defined at this
// location.
//
// Each element is a field number or an index. They form a path from
// the root FileDescriptorProto to the place where the definition. For
// example, this path:
// [ 4, 3, 2, 7, 1 ]
// refers to:
// file.message_type(3) // 4, 3
// .field(7) // 2, 7
// .name() // 1
// This is because FileDescriptorProto.message_type has field number 4:
// repeated DescriptorProto message_type = 4;
// and DescriptorProto.field has field number 2:
// repeated FieldDescriptorProto field = 2;
// and FieldDescriptorProto.name has field number 1:
// optional string name = 1;
//
// Thus, the above path gives the location of a field name. If we removed
// the last element:
// [ 4, 3, 2, 7 ]
// this path refers to the whole field declaration (from the beginning
// of the label to the terminating semicolon).
repeated int32 path = 1 [packed = true];
// Always has exactly three or four elements: start line, start column,
// end line (optional, otherwise assumed same as start line), end column.
// These are packed into a single field for efficiency. Note that line
// and column numbers are zero-based -- typically you will want to add
// 1 to each before displaying to a user.
repeated int32 span = 2 [packed = true];
// If this SourceCodeInfo represents a complete declaration, these are any
// comments appearing before and after the declaration which appear to be
// attached to the declaration.
//
// A series of line comments appearing on consecutive lines, with no other
// tokens appearing on those lines, will be treated as a single comment.
//
// leading_detached_comments will keep paragraphs of comments that appear
// before (but not connected to) the current element. Each paragraph,
// separated by empty lines, will be one comment element in the repeated
// field.
//
// Only the comment content is provided; comment markers (e.g. //) are
// stripped out. For block comments, leading whitespace and an asterisk
// will be stripped from the beginning of each line other than the first.
// Newlines are included in the output.
//
// Examples:
//
// optional int32 foo = 1; // Comment attached to foo.
// // Comment attached to bar.
// optional int32 bar = 2;
//
// optional string baz = 3;
// // Comment attached to baz.
// // Another line attached to baz.
//
// // Comment attached to qux.
// //
// // Another line attached to qux.
// optional double qux = 4;
//
// // Detached comment for corge. This is not leading or trailing comments
// // to qux or corge because there are blank lines separating it from
// // both.
//
// // Detached comment for corge paragraph 2.
//
// optional string corge = 5;
// /* Block comment attached
// * to corge. Leading asterisks
// * will be removed. */
// /* Block comment attached to
// * grault. */
// optional int32 grault = 6;
//
// // ignored detached comments.
optional string leading_comments = 3;
optional string trailing_comments = 4;
repeated string leading_detached_comments = 6;
}
}
// Describes the relationship between generated code and its original source
// file. A GeneratedCodeInfo message is associated with only one generated
// source file, but may contain references to different source .proto files.
message GeneratedCodeInfo {
// An Annotation connects some span of text in generated code to an element
// of its generating .proto file.
repeated Annotation annotation = 1;
message Annotation {
// Identifies the element in the original source .proto file. This field
// is formatted the same as SourceCodeInfo.Location.path.
repeated int32 path = 1 [packed = true];
// Identifies the filesystem path to the original source .proto.
optional string source_file = 2;
// Identifies the starting offset in bytes in the generated code
// that relates to the identified object.
optional int32 begin = 3;
// Identifies the ending offset in bytes in the generated code that
// relates to the identified offset. The end offset should be one past
// the last relevant byte (so the length of the text = end - begin).
optional int32 end = 4;
}
}

@ -0,0 +1,890 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// The messages in this file describe the definitions found in .proto files.
// A valid .proto file can be translated directly to a FileDescriptorProto
// without any other information (e.g. without reading its imports).
syntax = "proto2";
package upb_benchmark.sv;
option go_package = "google.golang.org/protobuf/types/descriptorpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
option csharp_namespace = "Google.Protobuf.Reflection";
option objc_class_prefix = "GPB";
option cc_enable_arenas = true;
// The protocol compiler can output a FileDescriptorSet containing the .proto
// files it parses.
message FileDescriptorSet {
repeated FileDescriptorProto file = 1;
}
// Describes a complete .proto file.
message FileDescriptorProto {
optional string name = 1
[ctype = STRING_PIECE]; // file name, relative to root of source tree
optional string package = 2
[ctype = STRING_PIECE]; // e.g. "foo", "foo.bar", etc.
// Names of files imported by this file.
repeated string dependency = 3 [ctype = STRING_PIECE];
// Indexes of the public imported files in the dependency list above.
repeated int32 public_dependency = 10;
// Indexes of the weak imported files in the dependency list.
// For Google-internal migration only. Do not use.
repeated int32 weak_dependency = 11;
// All top-level definitions in this file.
repeated DescriptorProto message_type = 4;
repeated EnumDescriptorProto enum_type = 5;
repeated ServiceDescriptorProto service = 6;
repeated FieldDescriptorProto extension = 7;
optional FileOptions options = 8;
// This field contains optional information about the original source code.
// You may safely remove this entire field without harming runtime
// functionality of the descriptors -- the information is needed only by
// development tools.
optional SourceCodeInfo source_code_info = 9;
// The syntax of the proto file.
// The supported values are "proto2" and "proto3".
optional string syntax = 12 [ctype = STRING_PIECE];
}
// Describes a message type.
message DescriptorProto {
optional string name = 1 [ctype = STRING_PIECE];
repeated FieldDescriptorProto field = 2;
repeated FieldDescriptorProto extension = 6;
repeated DescriptorProto nested_type = 3;
repeated EnumDescriptorProto enum_type = 4;
message ExtensionRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
optional ExtensionRangeOptions options = 3;
}
repeated ExtensionRange extension_range = 5;
repeated OneofDescriptorProto oneof_decl = 8;
optional MessageOptions options = 7;
// Range of reserved tag numbers. Reserved tag numbers may not be used by
// fields or extension ranges in the same message. Reserved ranges may
// not overlap.
message ReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Exclusive.
}
repeated ReservedRange reserved_range = 9;
// Reserved field names, which may not be used by fields in the same message.
// A given name may only be reserved once.
repeated string reserved_name = 10 [ctype = STRING_PIECE];
}
message ExtensionRangeOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// Describes a field within a message.
message FieldDescriptorProto {
enum Type {
// 0 is reserved for errors.
// Order is weird for historical reasons.
TYPE_DOUBLE = 1;
TYPE_FLOAT = 2;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
// negative values are likely.
TYPE_INT64 = 3;
TYPE_UINT64 = 4;
// Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
// negative values are likely.
TYPE_INT32 = 5;
TYPE_FIXED64 = 6;
TYPE_FIXED32 = 7;
TYPE_BOOL = 8;
TYPE_STRING = 9;
// Tag-delimited aggregate.
// Group type is deprecated and not supported in proto3. However, Proto3
// implementations should still be able to parse the group wire format and
// treat group fields as unknown fields.
TYPE_GROUP = 10;
TYPE_MESSAGE = 11; // Length-delimited aggregate.
// New in version 2.
TYPE_BYTES = 12;
TYPE_UINT32 = 13;
TYPE_ENUM = 14;
TYPE_SFIXED32 = 15;
TYPE_SFIXED64 = 16;
TYPE_SINT32 = 17; // Uses ZigZag encoding.
TYPE_SINT64 = 18; // Uses ZigZag encoding.
}
enum Label {
// 0 is reserved for errors
LABEL_OPTIONAL = 1;
LABEL_REQUIRED = 2;
LABEL_REPEATED = 3;
}
optional string name = 1 [ctype = STRING_PIECE];
optional int32 number = 3;
optional Label label = 4;
// If type_name is set, this need not be set. If both this and type_name
// are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
optional Type type = 5;
// For message and enum types, this is the name of the type. If the name
// starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
// rules are used to find the type (i.e. first the nested types within this
// message are searched, then within the parent, on up to the root
// namespace).
optional string type_name = 6 [ctype = STRING_PIECE];
// For extensions, this is the name of the type being extended. It is
// resolved in the same manner as type_name.
optional string extendee = 2 [ctype = STRING_PIECE];
// For numeric types, contains the original text representation of the value.
// For booleans, "true" or "false".
// For strings, contains the default text contents (not escaped in any way).
// For bytes, contains the C escaped value. All bytes >= 128 are escaped.
// TODO(kenton): Base-64 encode?
optional string default_value = 7 [ctype = STRING_PIECE];
// If set, gives the index of a oneof in the containing type's oneof_decl
// list. This field is a member of that oneof.
optional int32 oneof_index = 9;
// JSON name of this field. The value is set by protocol compiler. If the
// user has set a "json_name" option on this field, that option's value
// will be used. Otherwise, it's deduced from the field's name by converting
// it to camelCase.
optional string json_name = 10 [ctype = STRING_PIECE];
optional FieldOptions options = 8;
// If true, this is a proto3 "optional". When a proto3 field is optional, it
// tracks presence regardless of field type.
//
// When proto3_optional is true, this field must be belong to a oneof to
// signal to old proto3 clients that presence is tracked for this field. This
// oneof is known as a "synthetic" oneof, and this field must be its sole
// member (each proto3 optional field gets its own synthetic oneof). Synthetic
// oneofs exist in the descriptor only, and do not generate any API. Synthetic
// oneofs must be ordered after all "real" oneofs.
//
// For message fields, proto3_optional doesn't create any semantic change,
// since non-repeated message fields always track presence. However it still
// indicates the semantic detail of whether the user wrote "optional" or not.
// This can be useful for round-tripping the .proto file. For consistency we
// give message fields a synthetic oneof also, even though it is not required
// to track presence. This is especially important because the parser can't
// tell if a field is a message or an enum, so it must always create a
// synthetic oneof.
//
// Proto2 optional fields do not set this flag, because they already indicate
// optional with `LABEL_OPTIONAL`.
optional bool proto3_optional = 17;
}
// Describes a oneof.
message OneofDescriptorProto {
optional string name = 1 [ctype = STRING_PIECE];
optional OneofOptions options = 2;
}
// Describes an enum type.
message EnumDescriptorProto {
optional string name = 1 [ctype = STRING_PIECE];
repeated EnumValueDescriptorProto value = 2;
optional EnumOptions options = 3;
// Range of reserved numeric values. Reserved values may not be used by
// entries in the same enum. Reserved ranges may not overlap.
//
// Note that this is distinct from DescriptorProto.ReservedRange in that it
// is inclusive such that it can appropriately represent the entire int32
// domain.
message EnumReservedRange {
optional int32 start = 1; // Inclusive.
optional int32 end = 2; // Inclusive.
}
// Range of reserved numeric values. Reserved numeric values may not be used
// by enum values in the same enum declaration. Reserved ranges may not
// overlap.
repeated EnumReservedRange reserved_range = 4;
// Reserved enum value names, which may not be reused. A given name may only
// be reserved once.
repeated string reserved_name = 5 [ctype = STRING_PIECE];
}
// Describes a value within an enum.
message EnumValueDescriptorProto {
optional string name = 1 [ctype = STRING_PIECE];
optional int32 number = 2;
optional EnumValueOptions options = 3;
}
// Describes a service.
message ServiceDescriptorProto {
optional string name = 1 [ctype = STRING_PIECE];
repeated MethodDescriptorProto method = 2;
optional ServiceOptions options = 3;
}
// Describes a method of a service.
message MethodDescriptorProto {
optional string name = 1 [ctype = STRING_PIECE];
// Input and output type names. These are resolved in the same way as
// FieldDescriptorProto.type_name, but must refer to a message type.
optional string input_type = 2 [ctype = STRING_PIECE];
optional string output_type = 3 [ctype = STRING_PIECE];
optional MethodOptions options = 4;
// Identifies if client streams multiple client messages
optional bool client_streaming = 5 [default = false];
// Identifies if server streams multiple server messages
optional bool server_streaming = 6 [default = false];
}
// ===================================================================
// Options
// Each of the definitions above may have "options" attached. These are
// just annotations which may cause code to be generated slightly differently
// or may contain hints for code that manipulates protocol messages.
//
// Clients may define custom options as extensions of the *Options messages.
// These extensions may not yet be known at parsing time, so the parser cannot
// store the values in them. Instead it stores them in a field in the *Options
// message called uninterpreted_option. This field must have the same name
// across all *Options messages. We then use this field to populate the
// extensions when we build a descriptor, at which point all protos have been
// parsed and so all extensions are known.
//
// Extension numbers for custom options may be chosen as follows:
// * For options which will only be used within a single application or
// organization, or for experimental options, use field numbers 50000
// through 99999. It is up to you to ensure that you do not use the
// same number for multiple options.
// * For options which will be published and used publicly by multiple
// independent entities, e-mail protobuf-global-extension-registry@google.com
// to reserve extension numbers. Simply provide your project name (e.g.
// Objective-C plugin) and your project website (if available) -- there's no
// need to explain how you intend to use them. Usually you only need one
// extension number. You can declare multiple options with only one extension
// number by putting them in a sub-message. See the Custom Options section of
// the docs for examples:
// https://developers.google.com/protocol-buffers/docs/proto#options
// If this turns out to be popular, a web service will be set up
// to automatically assign option numbers.
message FileOptions {
// Sets the Java package where classes generated from this .proto will be
// placed. By default, the proto package is used, but this is often
// inappropriate because proto packages do not normally start with backwards
// domain names.
optional string java_package = 1 [ctype = STRING_PIECE];
// If set, all the classes from the .proto file are wrapped in a single
// outer class with the given name. This applies to both Proto1
// (equivalent to the old "--one_java_file" option) and Proto2 (where
// a .proto always translates to a single class, but you may want to
// explicitly choose the class name).
optional string java_outer_classname = 8 [ctype = STRING_PIECE];
// If set true, then the Java code generator will generate a separate .java
// file for each top-level message, enum, and service defined in the .proto
// file. Thus, these types will *not* be nested inside the outer class
// named by java_outer_classname. However, the outer class will still be
// generated to contain the file's getDescriptor() method as well as any
// top-level extensions defined in the file.
optional bool java_multiple_files = 10 [default = false];
// This option does nothing.
optional bool java_generate_equals_and_hash = 20 [deprecated = true];
// If set true, then the Java2 code generator will generate code that
// throws an exception whenever an attempt is made to assign a non-UTF-8
// byte sequence to a string field.
// Message reflection will do the same.
// However, an extension field still accepts non-UTF-8 byte sequences.
// This option has no effect on when used with the lite runtime.
optional bool java_string_check_utf8 = 27 [default = false];
// Generated classes can be optimized for speed or code size.
enum OptimizeMode {
SPEED = 1; // Generate complete code for parsing, serialization,
// etc.
CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
}
optional OptimizeMode optimize_for = 9 [default = SPEED];
// Sets the Go package where structs generated from this .proto will be
// placed. If omitted, the Go package will be derived from the following:
// - The basename of the package import path, if provided.
// - Otherwise, the package statement in the .proto file, if present.
// - Otherwise, the basename of the .proto file, without extension.
optional string go_package = 11 [ctype = STRING_PIECE];
// Should generic services be generated in each language? "Generic" services
// are not specific to any particular RPC system. They are generated by the
// main code generators in each language (without additional plugins).
// Generic services were the only kind of service generation supported by
// early versions of google.protobuf.
//
// Generic services are now considered deprecated in favor of using plugins
// that generate code specific to your particular RPC system. Therefore,
// these default to false. Old code which depends on generic services should
// explicitly set them to true.
optional bool cc_generic_services = 16 [default = false];
optional bool java_generic_services = 17 [default = false];
optional bool py_generic_services = 18 [default = false];
optional bool php_generic_services = 42 [default = false];
// Is this file deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for everything in the file, or it will be completely ignored; in the very
// least, this is a formalization for deprecating files.
optional bool deprecated = 23 [default = false];
// Enables the use of arenas for the proto messages in this file. This applies
// only to generated classes for C++.
optional bool cc_enable_arenas = 31 [default = true];
// Sets the objective c class prefix which is prepended to all objective c
// generated classes from this .proto. There is no default.
optional string objc_class_prefix = 36 [ctype = STRING_PIECE];
// Namespace for generated classes; defaults to the package.
optional string csharp_namespace = 37 [ctype = STRING_PIECE];
// By default Swift generators will take the proto package and CamelCase it
// replacing '.' with underscore and use that to prefix the types/symbols
// defined. When this options is provided, they will use this value instead
// to prefix the types/symbols defined.
optional string swift_prefix = 39 [ctype = STRING_PIECE];
// Sets the php class prefix which is prepended to all php generated classes
// from this .proto. Default is empty.
optional string php_class_prefix = 40 [ctype = STRING_PIECE];
// Use this option to change the namespace of php generated classes. Default
// is empty. When this option is empty, the package name will be used for
// determining the namespace.
optional string php_namespace = 41 [ctype = STRING_PIECE];
// Use this option to change the namespace of php generated metadata classes.
// Default is empty. When this option is empty, the proto file name will be
// used for determining the namespace.
optional string php_metadata_namespace = 44 [ctype = STRING_PIECE];
// Use this option to change the package of ruby generated classes. Default
// is empty. When this option is not set, the package name will be used for
// determining the ruby package.
optional string ruby_package = 45 [ctype = STRING_PIECE];
// The parser stores options it doesn't recognize here.
// See the documentation for the "Options" section above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message.
// See the documentation for the "Options" section above.
extensions 1000 to max;
reserved 38;
}
message MessageOptions {
// Set true to use the old proto1 MessageSet wire format for extensions.
// This is provided for backwards-compatibility with the MessageSet wire
// format. You should not use this for any other reason: It's less
// efficient, has fewer features, and is more complicated.
//
// The message must be defined exactly as follows:
// message Foo {
// option message_set_wire_format = true;
// extensions 4 to max;
// }
// Note that the message cannot have any defined fields; MessageSets only
// have extensions.
//
// All extensions of your type must be singular messages; e.g. they cannot
// be int32s, enums, or repeated messages.
//
// Because this is an option, the above two restrictions are not enforced by
// the protocol compiler.
optional bool message_set_wire_format = 1 [default = false];
// Disables the generation of the standard "descriptor()" accessor, which can
// conflict with a field of the same name. This is meant to make migration
// from proto1 easier; new code should avoid fields named "descriptor".
optional bool no_standard_descriptor_accessor = 2 [default = false];
// Is this message deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the message, or it will be completely ignored; in the very least,
// this is a formalization for deprecating messages.
optional bool deprecated = 3 [default = false];
// Whether the message is an automatically generated map entry type for the
// maps field.
//
// For maps fields:
// map<KeyType, ValueType> map_field = 1;
// The parsed descriptor looks like:
// message MapFieldEntry {
// option map_entry = true;
// optional KeyType key = 1;
// optional ValueType value = 2;
// }
// repeated MapFieldEntry map_field = 1;
//
// Implementations may choose not to generate the map_entry=true message, but
// use a native map in the target language to hold the keys and values.
// The reflection APIs in such implementations still need to work as
// if the field is a repeated message field.
//
// NOTE: Do not set the option in .proto files. Always use the maps syntax
// instead. The option should only be implicitly set by the proto compiler
// parser.
optional bool map_entry = 7;
reserved 8; // javalite_serializable
reserved 9; // javanano_as_lite
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message FieldOptions {
// The ctype option instructs the C++ code generator to use a different
// representation of the field than it normally would. See the specific
// options below. This option is not yet implemented in the open source
// release -- sorry, we'll try to include it in a future version!
optional CType ctype = 1 [default = STRING];
enum CType {
// Default mode.
STRING = 0;
CORD = 1;
STRING_PIECE = 2;
}
// The packed option can be enabled for repeated primitive fields to enable
// a more efficient representation on the wire. Rather than repeatedly
// writing the tag and type for each element, the entire array is encoded as
// a single length-delimited blob. In proto3, only explicit setting it to
// false will avoid using packed encoding.
optional bool packed = 2;
// The jstype option determines the JavaScript type used for values of the
// field. The option is permitted only for 64 bit integral and fixed types
// (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
// is represented as JavaScript string, which avoids loss of precision that
// can happen when a large value is converted to a floating point JavaScript.
// Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
// use the JavaScript "number" type. The behavior of the default option
// JS_NORMAL is implementation dependent.
//
// This option is an enum to permit additional types to be added, e.g.
// goog.math.Integer.
optional JSType jstype = 6 [default = JS_NORMAL];
enum JSType {
// Use the default type.
JS_NORMAL = 0;
// Use JavaScript strings.
JS_STRING = 1;
// Use JavaScript numbers.
JS_NUMBER = 2;
}
// Should this field be parsed lazily? Lazy applies only to message-type
// fields. It means that when the outer message is initially parsed, the
// inner message's contents will not be parsed but instead stored in encoded
// form. The inner message will actually be parsed when it is first accessed.
//
// This is only a hint. Implementations are free to choose whether to use
// eager or lazy parsing regardless of the value of this option. However,
// setting this option true suggests that the protocol author believes that
// using lazy parsing on this field is worth the additional bookkeeping
// overhead typically needed to implement it.
//
// This option does not affect the public interface of any generated code;
// all method signatures remain the same. Furthermore, thread-safety of the
// interface is not affected by this option; const methods remain safe to
// call from multiple threads concurrently, while non-const methods continue
// to require exclusive access.
//
//
// Note that implementations may choose not to check required fields within
// a lazy sub-message. That is, calling IsInitialized() on the outer message
// may return true even if the inner message has missing required fields.
// This is necessary because otherwise the inner message would have to be
// parsed in order to perform the check, defeating the purpose of lazy
// parsing. An implementation which chooses not to check required fields
// must be consistent about it. That is, for any particular sub-message, the
// implementation must either *always* check its required fields, or *never*
// check its required fields, regardless of whether or not the message has
// been parsed.
optional bool lazy = 5 [default = false];
// Is this field deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for accessors, or it will be completely ignored; in the very least, this
// is a formalization for deprecating fields.
optional bool deprecated = 3 [default = false];
// For Google-internal migration only. Do not use.
optional bool weak = 10 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
reserved 4; // removed jtype
}
message OneofOptions {
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumOptions {
// Set this option to true to allow mapping different tag names to the same
// value.
optional bool allow_alias = 2;
// Is this enum deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum, or it will be completely ignored; in the very least, this
// is a formalization for deprecating enums.
optional bool deprecated = 3 [default = false];
reserved 5; // javanano_as_lite
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message EnumValueOptions {
// Is this enum value deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the enum value, or it will be completely ignored; in the very least,
// this is a formalization for deprecating enum values.
optional bool deprecated = 1 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message ServiceOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this service deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the service, or it will be completely ignored; in the very least,
// this is a formalization for deprecating services.
optional bool deprecated = 33 [default = false];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
message MethodOptions {
// Note: Field numbers 1 through 32 are reserved for Google's internal RPC
// framework. We apologize for hoarding these numbers to ourselves, but
// we were already using them long before we decided to release Protocol
// Buffers.
// Is this method deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for the method, or it will be completely ignored; in the very least,
// this is a formalization for deprecating methods.
optional bool deprecated = 33 [default = false];
// Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
// or neither? HTTP based RPC implementation may choose GET verb for safe
// methods, and PUT verb for idempotent methods instead of the default POST.
enum IdempotencyLevel {
IDEMPOTENCY_UNKNOWN = 0;
NO_SIDE_EFFECTS = 1; // implies idempotent
IDEMPOTENT = 2; // idempotent, but may have side effects
}
optional IdempotencyLevel idempotency_level = 34
[default = IDEMPOTENCY_UNKNOWN];
// The parser stores options it doesn't recognize here. See above.
repeated UninterpretedOption uninterpreted_option = 999;
// Clients can define custom options in extensions of this message. See above.
extensions 1000 to max;
}
// A message representing a option the parser does not recognize. This only
// appears in options protos created by the compiler::Parser class.
// DescriptorPool resolves these when building Descriptor objects. Therefore,
// options protos in descriptor objects (e.g. returned by Descriptor::options(),
// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
// in them.
message UninterpretedOption {
// The name of the uninterpreted option. Each string represents a segment in
// a dot-separated name. is_extension is true iff a segment represents an
// extension (denoted with parentheses in options specs in .proto files).
// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
// "foo.(bar.baz).qux".
message NamePart {
optional string name_part = 1 [ctype = STRING_PIECE];
optional bool is_extension = 2;
}
repeated NamePart name = 2;
// The value of the uninterpreted option, in whatever type the tokenizer
// identified it as during parsing. Exactly one of these should be set.
optional string identifier_value = 3 [ctype = STRING_PIECE];
optional uint64 positive_int_value = 4;
optional int64 negative_int_value = 5;
optional double double_value = 6;
optional bytes string_value = 7;
optional string aggregate_value = 8 [ctype = STRING_PIECE];
}
// ===================================================================
// Optional source code info
// Encapsulates information about the original source file from which a
// FileDescriptorProto was generated.
message SourceCodeInfo {
// A Location identifies a piece of source code in a .proto file which
// corresponds to a particular definition. This information is intended
// to be useful to IDEs, code indexers, documentation generators, and similar
// tools.
//
// For example, say we have a file like:
// message Foo {
// optional string foo = 1 [ctype = STRING_PIECE];
// }
// Let's look at just the field definition:
// optional string foo = 1 [ctype = STRING_PIECE];
// ^ ^^ ^^ ^ ^^^
// a bc de f ghi
// We have the following locations:
// span path represents
// [a,i) [ 4, 0, 2, 0 ] The whole field definition.
// [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
// [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
// [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
// [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
//
// Notes:
// - A location may refer to a repeated field itself (i.e. not to any
// particular index within it). This is used whenever a set of elements are
// logically enclosed in a single code segment. For example, an entire
// extend block (possibly containing multiple extension definitions) will
// have an outer location whose path refers to the "extensions" repeated
// field without an index.
// - Multiple locations may have the same path. This happens when a single
// logical declaration is spread out across multiple places. The most
// obvious example is the "extend" block again -- there may be multiple
// extend blocks in the same scope, each of which will have the same path.
// - A location's span is not always a subset of its parent's span. For
// example, the "extendee" of an extension declaration appears at the
// beginning of the "extend" block and is shared by all extensions within
// the block.
// - Just because a location's span is a subset of some other location's span
// does not mean that it is a descendant. For example, a "group" defines
// both a type and a field in a single declaration. Thus, the locations
// corresponding to the type and field and their components will overlap.
// - Code which tries to interpret locations should probably be designed to
// ignore those that it doesn't understand, as more types of locations could
// be recorded in the future.
repeated Location location = 1;
message Location {
// Identifies which part of the FileDescriptorProto was defined at this
// location.
//
// Each element is a field number or an index. They form a path from
// the root FileDescriptorProto to the place where the definition. For
// example, this path:
// [ 4, 3, 2, 7, 1 ]
// refers to:
// file.message_type(3) // 4, 3
// .field(7) // 2, 7
// .name() // 1
// This is because FileDescriptorProto.message_type has field number 4:
// repeated DescriptorProto message_type = 4;
// and DescriptorProto.field has field number 2:
// repeated FieldDescriptorProto field = 2;
// and FieldDescriptorProto.name has field number 1:
// optional string name = 1 [ctype = STRING_PIECE];
//
// Thus, the above path gives the location of a field name. If we removed
// the last element:
// [ 4, 3, 2, 7 ]
// this path refers to the whole field declaration (from the beginning
// of the label to the terminating semicolon).
repeated int32 path = 1 [packed = true];
// Always has exactly three or four elements: start line, start column,
// end line (optional, otherwise assumed same as start line), end column.
// These are packed into a single field for efficiency. Note that line
// and column numbers are zero-based -- typically you will want to add
// 1 to each before displaying to a user.
repeated int32 span = 2 [packed = true];
// If this SourceCodeInfo represents a complete declaration, these are any
// comments appearing before and after the declaration which appear to be
// attached to the declaration.
//
// A series of line comments appearing on consecutive lines, with no other
// tokens appearing on those lines, will be treated as a single comment.
//
// leading_detached_comments will keep paragraphs of comments that appear
// before (but not connected to) the current element. Each paragraph,
// separated by empty lines, will be one comment element in the repeated
// field.
//
// Only the comment content is provided; comment markers (e.g. //) are
// stripped out. For block comments, leading whitespace and an asterisk
// will be stripped from the beginning of each line other than the first.
// Newlines are included in the output.
//
// Examples:
//
// optional int32 foo = 1; // Comment attached to foo.
// // Comment attached to bar.
// optional int32 bar = 2;
//
// optional string baz = 3 [ctype = STRING_PIECE];
// // Comment attached to baz.
// // Another line attached to baz.
//
// // Comment attached to qux.
// //
// // Another line attached to qux.
// optional double qux = 4;
//
// // Detached comment for corge. This is not leading or trailing comments
// // to qux or corge because there are blank lines separating it from
// // both.
//
// // Detached comment for corge paragraph 2.
//
// optional string corge = 5 [ctype = STRING_PIECE];
// /* Block comment attached
// * to corge. Leading asterisks
// * will be removed. */
// /* Block comment attached to
// * grault. */
// optional int32 grault = 6;
//
// // ignored detached comments.
optional string leading_comments = 3 [ctype = STRING_PIECE];
optional string trailing_comments = 4 [ctype = STRING_PIECE];
repeated string leading_detached_comments = 6 [ctype = STRING_PIECE];
}
}
// Describes the relationship between generated code and its original source
// file. A GeneratedCodeInfo message is associated with only one generated
// source file, but may contain references to different source .proto files.
message GeneratedCodeInfo {
// An Annotation connects some span of text in generated code to an element
// of its generating .proto file.
repeated Annotation annotation = 1;
message Annotation {
// Identifies the element in the original source .proto file. This field
// is formatted the same as SourceCodeInfo.Location.path.
repeated int32 path = 1 [packed = true];
// Identifies the filesystem path to the original source .proto.
optional string source_file = 2 [ctype = STRING_PIECE];
// Identifies the starting offset in bytes in the generated code
// that relates to the identified object.
optional int32 begin = 3;
// Identifies the ending offset in bytes in the generated code that
// relates to the identified offset. The end offset should be one past
// the last relevant byte (so the length of the text = end - begin).
optional int32 end = 4;
}
}

@ -0,0 +1,35 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package upb_benchmark;
message Empty {}

@ -0,0 +1,69 @@
#!/usr/bin/python3
#
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
import re
include = sys.argv[1]
msg_basename = sys.argv[2]
count = 1
m = re.search(r'(.*\D)(\d+)$', sys.argv[2])
if m:
msg_basename = m.group(1)
count = int(m.group(2))
print('''
#include "{include}"
char buf[1];
int main() {{
'''.format(include=include))
def RefMessage(name):
print('''
{{
{name} proto;
proto.ParseFromArray(buf, 0);
proto.SerializePartialToArray(&buf[0], 0);
}}
'''.format(name=name))
RefMessage(msg_basename)
for i in range(2, count + 1):
RefMessage(msg_basename + str(i))
print('''
return 0;
}''')

@ -0,0 +1,123 @@
#!/usr/bin/python3
#
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
import random
base = sys.argv[1]
field_freqs = [
(('bool', 'optional'), 8.321),
(('bool', 'repeated'), 0.033),
(('bytes', 'optional'), 0.809),
(('bytes', 'repeated'), 0.065),
(('double', 'optional'), 2.845),
(('double', 'repeated'), 0.143),
(('fixed32', 'optional'), 0.084),
(('fixed32', 'repeated'), 0.012),
(('fixed64', 'optional'), 0.204),
(('fixed64', 'repeated'), 0.027),
(('float', 'optional'), 2.355),
(('float', 'repeated'), 0.132),
(('int32', 'optional'), 6.717),
(('int32', 'repeated'), 0.366),
(('int64', 'optional'), 9.678),
(('int64', 'repeated'), 0.425),
(('sfixed32', 'optional'), 0.018),
(('sfixed32', 'repeated'), 0.005),
(('sfixed64', 'optional'), 0.022),
(('sfixed64', 'repeated'), 0.005),
(('sint32', 'optional'), 0.026),
(('sint32', 'repeated'), 0.009),
(('sint64', 'optional'), 0.018),
(('sint64', 'repeated'), 0.006),
(('string', 'optional'), 25.461),
(('string', 'repeated'), 2.606),
(('Enum', 'optional'), 6.16),
(('Enum', 'repeated'), 0.576),
(('Message', 'optional'), 22.472),
(('Message', 'repeated'), 7.766),
(('uint32', 'optional'), 1.289),
(('uint32', 'repeated'), 0.051),
(('uint64', 'optional'), 1.044),
(('uint64', 'repeated'), 0.079),
]
population = [item[0] for item in field_freqs]
weights = [item[1] for item in field_freqs]
def choices(k):
if sys.version_info >= (3, 6):
return random.choices(population=population, weights=weights, k=k)
else:
print("WARNING: old Python version, field types are not properly weighted!")
return [random.choice(population) for _ in range(k)]
with open(base + "/100_msgs.proto", "w") as f:
f.write('syntax = "proto3";\n')
f.write('package upb_benchmark;\n')
f.write('message Message {}\n')
for i in range(2, 101):
f.write('message Message{i} {{}}\n'.format(i=i))
with open(base + "/200_msgs.proto", "w") as f:
f.write('syntax = "proto3";\n')
f.write('package upb_benchmark;\n')
f.write('message Message {}\n')
for i in range(2, 501):
f.write('message Message{i} {{}}\n'.format(i=i))
with open(base + "/100_fields.proto", "w") as f:
f.write('syntax = "proto2";\n')
f.write('package upb_benchmark;\n')
f.write('enum Enum { ZERO = 0; }\n')
f.write('message Message {\n')
i = 1
random.seed(a=0, version=2)
for field in choices(100):
field_type, label = field
f.write(' {label} {field_type} field{i} = {i};\n'.format(i=i, label=label, field_type=field_type))
i += 1
f.write('}\n')
with open(base + "/200_fields.proto", "w") as f:
f.write('syntax = "proto2";\n')
f.write('package upb_benchmark;\n')
f.write('enum Enum { ZERO = 0; }\n')
f.write('message Message {\n')
i = 1
random.seed(a=0, version=2)
for field in choices(200):
field_type, label = field
f.write(' {label} {field_type} field{i} = {i};\n'.format(i=i, label=label,field_type=field_type))
i += 1
f.write('}\n')

@ -0,0 +1,70 @@
#!/usr/bin/python3
#
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
import re
include = sys.argv[1]
msg_basename = sys.argv[2]
count = 1
m = re.search(r'(.*\D)(\d+)$', sys.argv[2])
if m:
msg_basename = m.group(1)
count = int(m.group(2))
print('''
#include "{include}"
char buf[1];
int main() {{
upb_Arena *arena = upb_Arena_New();
size_t size;
'''.format(include=include))
def RefMessage(name):
print('''
{{
{name} *proto = {name}_parse(buf, 1, arena);
{name}_serialize(proto, arena, &size);
}}
'''.format(name=name))
RefMessage(msg_basename)
for i in range(2, count + 1):
RefMessage(msg_basename + str(i))
print('''
return 0;
}''')

@ -0,0 +1,125 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load(
":build_defs.bzl",
"staleness_test",
)
load(
"//bazel:build_defs.bzl",
"make_shell_script",
)
licenses(["notice"])
exports_files(["staleness_test.py"])
py_library(
name = "staleness_test_lib",
testonly = 1,
srcs = ["staleness_test_lib.py"],
# This is public only for use by the staleness_test() macro. Please do not
# depend on this target directly.
visibility = ["//visibility:public"],
)
py_binary(
name = "make_cmakelists",
srcs = ["make_cmakelists.py"],
)
genrule(
name = "gen_cmakelists",
srcs = [
"//:BUILD",
"//:WORKSPACE",
"//bazel:workspace_deps.bzl",
],
outs = ["generated-in/CMakeLists.txt"],
cmd = "$(location :make_cmakelists) " +
"$(location //bazel:workspace_deps.bzl) " +
"$(location //:WORKSPACE) " +
"$(location //:BUILD) $@",
tools = [":make_cmakelists"],
)
genrule(
name = "copy_protos",
srcs = ["//:descriptor_upb_proto"],
outs = [
"generated-in/google/protobuf/descriptor.upb.c",
"generated-in/google/protobuf/descriptor.upb.h",
],
cmd = "cp $(SRCS) $(@D)/generated-in/google/protobuf",
)
staleness_test(
name = "test_generated_files",
outs = [
"CMakeLists.txt",
"google/protobuf/descriptor.upb.c",
"google/protobuf/descriptor.upb.h",
],
generated_pattern = "generated-in/%s",
tags = ["manual"],
)
# Test the CMake build #########################################################
make_shell_script(
name = "gen_run_cmake_build",
out = "run_cmake_build.sh",
contents = "set -ex\n" +
"cd $(dirname $1) && cp -r . .. && cd ../..\n" +
"mkdir build && cd build && cmake ../cmake && make -j8 && make test",
)
sh_test(
name = "cmake_build",
srcs = ["run_cmake_build.sh"],
args = ["$(location :gen_cmakelists)"],
data = [
":copy_protos",
":gen_cmakelists",
"//:source_files",
"//upb/base:source_files",
"//upb/collections:source_files",
"//upb/hash:source_files",
"//upb/lex:source_files",
"//upb/mem:source_files",
"//upb/message:source_files",
"//upb/mini_descriptor:source_files",
"//upb/mini_table:source_files",
"//upb/port:source_files",
"//upb/text:source_files",
"//upb/wire:source_files",
"@utf8_range//:utf8_range_srcs",
],
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}),
deps = ["@bazel_tools//tools/bash/runfiles"],
)

@ -0,0 +1,129 @@
# This file was generated from BUILD using tools/make_cmakelists.py.
cmake_minimum_required(VERSION 3.10...3.24)
project(upb)
set(CMAKE_C_STANDARD 99)
# Prevent CMake from setting -rdynamic on Linux (!!).
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
SET(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
# Set default build type.
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.")
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
FORCE)
endif()
# When using Ninja, compiler output won't be colorized without this.
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(-fdiagnostics-color=always SUPPORTS_COLOR_ALWAYS)
if(SUPPORTS_COLOR_ALWAYS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
endif()
# Implement ASAN/UBSAN options
if(UPB_ENABLE_ASAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
endif()
if(UPB_ENABLE_UBSAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
endif()
if(NOT TARGET utf8_range)
if(EXISTS ../external/utf8_range)
# utf8_range is already installed
include_directories(../external/utf8_range)
elseif(EXISTS ../../utf8_range)
include_directories(../../utf8_range)
else()
include(FetchContent)
FetchContent_Declare(
utf8_range
GIT_REPOSITORY "https://github.com/protocolbuffers/utf8_range.git"
GIT_TAG "de0b4a8ff9b5d4c98108bdfe723291a33c52c54f"
)
FetchContent_GetProperties(utf8_range)
if(NOT utf8_range_POPULATED)
FetchContent_Populate(utf8_range)
include_directories(${utf8_range_SOURCE_DIR})
endif()
endif()
endif()
if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup -flat_namespace")
elseif(UNIX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--build-id")
endif()
enable_testing()
add_library(upb INTERFACE
)
target_include_directories(upb INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../cmake>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINRARY_DIR}>
)
target_link_libraries(upb INTERFACE
base
mem)
add_library(generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
)
target_include_directories(generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../cmake>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINRARY_DIR}>
)
target_link_libraries(generated_code_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
base
collections
collections_internal
mem
message
message_accessors
message_accessors_internal
message_internal
mini_descriptor
mini_table
wire
wire_internal)
add_library(generated_cpp_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
)
target_include_directories(generated_cpp_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../cmake>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINRARY_DIR}>
)
add_library(generated_reflection_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
)
target_include_directories(generated_reflection_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../cmake>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINRARY_DIR}>
)
target_link_libraries(generated_reflection_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me INTERFACE
mem
mini_descriptor
reflection_internal)

@ -0,0 +1,23 @@
# upb CMake build (EXPERIMENTAL)
upb's CMake support is experimental. The core library builds successfully
under CMake, and this is verified by the Bazel tests in this directory.
However there is no support for building the upb compiler or for generating
.upb.c/upb.h files. This means upb's CMake support is incomplete at best,
unless your application is intended to be purely reflective.
If you find this CMake setup useful in its current state, please consider
filing an issue so we know. If you have suggestions for how it could be
more useful (and particularly if you can contribute some code for it)
please feel free to file an issue for that too. Do keep in mind that upb
does not currently provide any ABI stability, so we want to avoid providing
a shared library.
The CMakeLists.txt is generated from the Bazel BUILD files using the Python
scripts in this directory. We want to avoid having two separate sources of
truth that both need to be updated when a file is added or removed.
This directory also contains some generated files that would be created
on the fly during a Bazel build. These are automaticaly kept in sync by
the Bazel test `//cmake:test_generated_files`.

@ -0,0 +1,77 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Bazel support functions related to CMake support."""
def staleness_test(name, outs, generated_pattern, target_files = None, tags = [], **kwargs):
"""Tests that checked-in file(s) match the contents of generated file(s).
The resulting test will verify that all output files exist and have the
correct contents. If the test fails, it can be invoked with --fix to
bring the checked-in files up to date.
Args:
name: Name of the rule.
outs: the checked-in files that are copied from generated files.
generated_pattern: the pattern for transforming each "out" file into a
generated file. For example, if generated_pattern="generated/%s" then
a file foo.txt will look for generated file generated/foo.txt.
target_files: A glob representing all of the files that should be
covered by this rule. Files in this glob but not generated will
be deleted. (Not currently implemented in OSS).
**kwargs: Additional keyword arguments to pass through to py_test().
"""
script_name = name + ".py"
script_src = Label("//cmake:staleness_test.py")
# Filter out non-existing rules so Blaze doesn't error out before we even
# run the test.
existing_outs = native.glob(include = outs)
# The file list contains a few extra bits of information at the end.
# These get unpacked by the Config class in staleness_test_lib.py.
file_list = outs + [generated_pattern, native.package_name() or ".", name]
native.genrule(
name = name + "_makescript",
outs = [script_name],
srcs = [script_src],
testonly = 1,
cmd = "cp $< $@; " +
"sed -i.bak -e 's|INSERT_FILE_LIST_HERE|" + "\\\n ".join(file_list) + "|' $@",
)
native.py_test(
name = name,
srcs = [script_name],
data = existing_outs + [generated_pattern % file for file in outs],
python_version = "PY3",
deps = [
Label("//cmake:staleness_test_lib"),
],
tags = ["staleness_test"] + tags,
**kwargs
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,420 @@
#!/usr/bin/python
#
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""A tool to convert {WORKSPACE, BUILD} -> CMakeLists.txt.
This tool is very upb-specific at the moment, and should not be seen as a
generic Bazel -> CMake converter.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import sys
import textwrap
import os
def StripFirstChar(deps):
return [dep[1:] for dep in deps]
def IsSourceFile(name):
return name.endswith(".c") or name.endswith(".cc")
ADD_LIBRARY_FORMAT = """
add_library(%(name)s %(type)s
%(sources)s
)
target_include_directories(%(name)s %(keyword)s
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../cmake>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINRARY_DIR}>
)
"""
class BuildFileFunctions(object):
def __init__(self, converter):
self.converter = converter
def _add_deps(self, kwargs, keyword=""):
if "deps" not in kwargs:
return
self.converter.toplevel += "target_link_libraries(%s%s\n %s)\n" % (
kwargs["name"],
keyword,
"\n ".join(StripFirstChar(kwargs["deps"]))
)
def load(self, *args):
pass
def cc_library(self, **kwargs):
if kwargs["name"].endswith("amalgamation"):
return
if kwargs["name"] == "upbc_generator":
return
if kwargs["name"] == "lupb":
return
if "testonly" in kwargs:
return
files = kwargs.get("srcs", []) + kwargs.get("hdrs", [])
found_files = []
pregenerated_files = [
"CMakeLists.txt", "descriptor.upb.h", "descriptor.upb.c"
]
for file in files:
if os.path.basename(file) in pregenerated_files:
found_files.append("../cmake/" + file)
else:
found_files.append("../" + file)
if list(filter(IsSourceFile, files)):
# Has sources, make this a normal library.
self.converter.toplevel += ADD_LIBRARY_FORMAT % {
"name": kwargs["name"],
"type": "",
"keyword": "PUBLIC",
"sources": "\n ".join(found_files),
}
self._add_deps(kwargs)
else:
# Header-only library, have to do a couple things differently.
# For some info, see:
# http://mariobadr.com/creating-a-header-only-library-with-cmake.html
self.converter.toplevel += ADD_LIBRARY_FORMAT % {
"name": kwargs["name"],
"type": "INTERFACE",
"keyword": "INTERFACE",
"sources": "",
}
self._add_deps(kwargs, " INTERFACE")
def cc_binary(self, **kwargs):
pass
def cc_test(self, **kwargs):
# Disable this until we properly support upb_proto_library().
# self.converter.toplevel += "add_executable(%s\n %s)\n" % (
# kwargs["name"],
# "\n ".join(kwargs["srcs"])
# )
# self.converter.toplevel += "add_test(NAME %s COMMAND %s)\n" % (
# kwargs["name"],
# kwargs["name"],
# )
# if "data" in kwargs:
# for data_dep in kwargs["data"]:
# self.converter.toplevel += textwrap.dedent("""\
# add_custom_command(
# TARGET %s POST_BUILD
# COMMAND ${CMAKE_COMMAND} -E copy
# ${CMAKE_SOURCE_DIR}/%s
# ${CMAKE_CURRENT_BINARY_DIR}/%s)\n""" % (
# kwargs["name"], data_dep, data_dep
# ))
# self._add_deps(kwargs)
pass
def cc_fuzz_test(self, **kwargs):
pass
def pkg_files(self, **kwargs):
pass
def py_library(self, **kwargs):
pass
def py_binary(self, **kwargs):
pass
def lua_proto_library(self, **kwargs):
pass
def sh_test(self, **kwargs):
pass
def make_shell_script(self, **kwargs):
pass
def exports_files(self, files, **kwargs):
pass
def proto_library(self, **kwargs):
pass
def cc_proto_library(self, **kwargs):
pass
def staleness_test(self, **kwargs):
pass
def upb_amalgamation(self, **kwargs):
pass
def upb_proto_library(self, **kwargs):
pass
def upb_proto_library_copts(self, **kwargs):
pass
def upb_proto_reflection_library(self, **kwargs):
pass
def upb_proto_srcs(self, **kwargs):
pass
def genrule(self, **kwargs):
pass
def config_setting(self, **kwargs):
pass
def upb_fasttable_enabled(self, **kwargs):
pass
def select(self, arg_dict):
return []
def glob(self, *args, **kwargs):
return []
def licenses(self, *args):
pass
def filegroup(self, **kwargs):
pass
def map_dep(self, arg):
return arg
def package_group(self, **kwargs):
pass
def bool_flag(self, **kwargs):
pass
def bootstrap_upb_proto_library(self, **kwargs):
pass
def bootstrap_cc_library(self, **kwargs):
pass
def alias(self, **kwargs):
pass
class WorkspaceFileFunctions(object):
def __init__(self, converter):
self.converter = converter
def load(self, *args, **kwargs):
pass
def workspace(self, **kwargs):
self.converter.prelude += "project(%s)\n" % (kwargs["name"])
self.converter.prelude += "set(CMAKE_C_STANDARD 99)\n"
def maybe(self, rule, **kwargs):
if kwargs["name"] == "utf8_range":
self.converter.utf8_range_commit = kwargs["commit"]
pass
def http_archive(self, **kwargs):
pass
def git_repository(self, **kwargs):
pass
def new_git_repository(self, **kwargs):
pass
def bazel_version_repository(self, **kwargs):
pass
def protobuf_deps(self):
pass
def utf8_range_deps(self):
pass
def pip_parse(self, **kwargs):
pass
def rules_fuzzing_dependencies(self):
pass
def rules_fuzzing_init(self):
pass
def rules_pkg_dependencies(self):
pass
def system_python(self, **kwargs):
pass
def register_system_python(self, **kwargs):
pass
def register_toolchains(self, toolchain):
pass
def python_source_archive(self, **kwargs):
pass
def python_nuget_package(self, **kwargs):
pass
def install_deps(self):
pass
def fuzzing_py_install_deps(self):
pass
def googletest_deps(self):
pass
class Converter(object):
def __init__(self):
self.prelude = ""
self.toplevel = ""
self.if_lua = ""
self.utf8_range_commit = ""
def convert(self):
return self.template % {
"prelude": converter.prelude,
"toplevel": converter.toplevel,
"utf8_range_commit": converter.utf8_range_commit,
}
template = textwrap.dedent("""\
# This file was generated from BUILD using tools/make_cmakelists.py.
cmake_minimum_required(VERSION 3.10...3.24)
%(prelude)s
# Prevent CMake from setting -rdynamic on Linux (!!).
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
SET(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
# Set default build type.
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.")
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
FORCE)
endif()
# When using Ninja, compiler output won't be colorized without this.
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(-fdiagnostics-color=always SUPPORTS_COLOR_ALWAYS)
if(SUPPORTS_COLOR_ALWAYS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
endif()
# Implement ASAN/UBSAN options
if(UPB_ENABLE_ASAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
endif()
if(UPB_ENABLE_UBSAN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
endif()
if(NOT TARGET utf8_range)
if(EXISTS ../external/utf8_range)
# utf8_range is already installed
include_directories(../external/utf8_range)
elseif(EXISTS ../../utf8_range)
include_directories(../../utf8_range)
else()
include(FetchContent)
FetchContent_Declare(
utf8_range
GIT_REPOSITORY "https://github.com/protocolbuffers/utf8_range.git"
GIT_TAG "%(utf8_range_commit)s"
)
FetchContent_GetProperties(utf8_range)
if(NOT utf8_range_POPULATED)
FetchContent_Populate(utf8_range)
include_directories(${utf8_range_SOURCE_DIR})
endif()
endif()
endif()
if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup -flat_namespace")
elseif(UNIX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--build-id")
endif()
enable_testing()
%(toplevel)s
""")
data = {}
converter = Converter()
def GetDict(obj):
ret = {}
ret["UPB_DEFAULT_COPTS"] = [] # HACK
ret["UPB_DEFAULT_CPPOPTS"] = [] # HACK
for k in dir(obj):
if not k.startswith("_"):
ret[k] = getattr(obj, k);
return ret
globs = GetDict(converter)
workspace_dict = GetDict(WorkspaceFileFunctions(converter))
# We take all file paths as command-line arguments to ensure that we can find
# each file regardless of how exactly Bazel was invoked.
exec(open(sys.argv[1]).read(), workspace_dict) # workspace_deps.bzl
exec(open(sys.argv[2]).read(), workspace_dict) # WORKSPACE
exec(open(sys.argv[3]).read(), GetDict(BuildFileFunctions(converter))) # BUILD
with open(sys.argv[4], "w") as f:
f.write(converter.convert())

@ -0,0 +1,73 @@
#!/bin/bash
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This script updates checked-in generated files (currently CMakeLists.txt,
# descriptor.upb.h, and descriptor.upb.c), commits the resulting change, and
# pushes it. This does not do anything useful when run manually, but should be
# run by our GitHub action instead.
set -ex
# Exit early if the previous commit was made by the bot. This reduces the risk
# of a bug causing an infinite loop of auto-generated commits.
if (git log -1 --pretty=format:'%an' | grep -q "Protobuf Team Bot"); then
echo "Previous commit was authored by bot"
exit 0
fi
cd $(dirname -- "$0")/..
bazel test //cmake:test_generated_files || bazel-bin/cmake/test_generated_files --fix
# Try to determine the most recent pull request number.
title=$(git log -1 --pretty='%s')
pr_from_merge=$(echo "$title" | sed -n 's/^Merge pull request #\([0-9]\+\).*/\1/p')
pr_from_squash=$(echo "$title" | sed -n 's/^.*(#\([0-9]\+\))$/\1/p')
pr_number=""
if [ ! -z "$pr_from_merge" ]; then
pr_number="$pr_from_merge"
elif [ ! -z "$pr_from_squash" ]; then
pr_number="$pr_from_squash"
fi
if [ ! -z "$pr_number" ]; then
commit_message="Auto-generate CMake file lists after PR #$pr_number"
else
# If we are unable to determine the pull request number, we fall back on this
# default commit message. Typically this should not occur, but could happen
# if a pull request was merged via a rebase.
commit_message="Auto-generate CMake file lists"
fi
git add -A
git diff --staged --quiet || git commit -am "$commit_message"
git push

@ -0,0 +1,60 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""The py_test() script for staleness_test() rules.
Note that this file is preprocessed! The INSERT_<...> text below is replaced
with the actual list of files before we actually run the script.
"""
from __future__ import absolute_import
from cmake import staleness_test_lib
import unittest
import sys
file_list = """
INSERT_FILE_LIST_HERE
""".split()
config = staleness_test_lib.Config(file_list)
class TestFilesMatch(unittest.TestCase):
def testFilesMatch(self):
errors = staleness_test_lib.CheckFilesMatch(config)
self.assertFalse(errors, errors)
if len(sys.argv) > 1 and sys.argv[1] == "--fix":
staleness_test_lib.FixFiles(config)
else:
unittest.main()

@ -0,0 +1,194 @@
#!/usr/bin/python
#
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Shared code for validating staleness_test() rules.
This code is used by test scripts generated from staleness_test() rules.
"""
from __future__ import absolute_import
from __future__ import print_function
import difflib
import sys
import os
from shutil import copyfile
class _FilePair(object):
"""Represents a single (target, generated) file pair."""
def __init__(self, target, generated):
self.target = target
self.generated = generated
class Config(object):
"""Represents the configuration for a single staleness test target."""
def __init__(self, file_list):
# Duplicate to avoid modifying our arguments.
file_list = list(file_list)
# The file list contains a few other bits of information at the end.
# This is packed by the code in build_defs.bzl.
self.target_name = file_list.pop()
self.package_name = file_list.pop()
self.pattern = file_list.pop()
self.file_list = file_list
def _GetFilePairs(config):
"""Generates the list of file pairs.
Args:
config: a Config object representing this target's config.
Returns:
A list of _FilePair objects.
"""
ret = []
has_bazel_genfiles = os.path.exists("bazel-bin")
for filename in config.file_list:
target = os.path.join(config.package_name, filename)
generated = os.path.join(config.package_name, config.pattern % filename)
if has_bazel_genfiles:
generated = os.path.join("bazel-bin", generated)
# Generated files should always exist. Blaze should guarantee this before
# we are run.
if not os.path.isfile(generated):
print("Generated file '%s' does not exist." % generated)
print("Please run this command to generate it:")
print(" bazel build %s:%s" % (config.package_name, config.target_name))
sys.exit(1)
ret.append(_FilePair(target, generated))
return ret
def _GetMissingAndStaleFiles(file_pairs):
"""Generates lists of missing and stale files.
Args:
file_pairs: a list of _FilePair objects.
Returns:
missing_files: a list of _FilePair objects representing missing files.
These target files do not exist at all.
stale_files: a list of _FilePair objects representing stale files.
These target files exist but have stale contents.
"""
missing_files = []
stale_files = []
for pair in file_pairs:
if not os.path.isfile(pair.target):
missing_files.append(pair)
continue
with open(pair.generated) as g, open(pair.target) as t:
if g.read() != t.read():
stale_files.append(pair)
return missing_files, stale_files
def _CopyFiles(file_pairs):
"""Copies all generated files to the corresponding target file.
The target files must be writable already.
Args:
file_pairs: a list of _FilePair objects that we want to copy.
"""
for pair in file_pairs:
target_dir = os.path.dirname(pair.target)
if not os.path.isdir(target_dir):
os.makedirs(target_dir)
copyfile(pair.generated, pair.target)
def FixFiles(config):
"""Implements the --fix option: overwrites missing or out-of-date files.
Args:
config: the Config object for this test.
"""
file_pairs = _GetFilePairs(config)
missing_files, stale_files = _GetMissingAndStaleFiles(file_pairs)
_CopyFiles(stale_files + missing_files)
def CheckFilesMatch(config):
"""Checks whether each target file matches the corresponding generated file.
Args:
config: the Config object for this test.
Returns:
None if everything matches, otherwise a string error message.
"""
diff_errors = []
file_pairs = _GetFilePairs(config)
missing_files, stale_files = _GetMissingAndStaleFiles(file_pairs)
for pair in missing_files:
diff_errors.append("File %s does not exist" % pair.target)
continue
for pair in stale_files:
with open(pair.generated) as g, open(pair.target) as t:
diff = ''.join(difflib.unified_diff(g.read().splitlines(keepends=True),
t.read().splitlines(keepends=True)))
diff_errors.append("File %s is out of date:\n%s" % (pair.target, diff))
if diff_errors:
error_msg = "Files out of date!\n\n"
error_msg += "To fix run THIS command:\n"
error_msg += " bazel-bin/%s/%s --fix\n\n" % (config.package_name,
config.target_name)
error_msg += "Errors:\n"
error_msg += " " + "\n ".join(diff_errors)
return error_msg
else:
return None

@ -0,0 +1,429 @@
# upb Design
[TOC]
upb is a protobuf kernel written in C. It is a fast and conformant
implementation of protobuf, with a low-level C API that is designed to be
wrapped in other languages.
upb is not designed to be used by applications directly. The C API is very
low-level, unsafe, and changes frequently. It is important that upb is able to
make breaking API changes as necessary, to avoid taking on technical debt that
would compromise upb's goals of small code size and fast performance.
## Design goals
Goals:
- Full protobuf conformance
- Small code size
- Fast performance (without compromising code size)
- Easy to wrap in language runtimes
- Easy to adapt to different memory management schemes (refcounting, GC, etc)
Non-Goals:
- Stable API
- Safe API
- Ergonomic API for applications
Parameters:
- C99
- 32 or 64-bit CPU (assumes 4 or 8 byte pointers)
- Uses pointer tagging, but avoids other implementation-defined behavior
- Aims to never invoke undefined behavior (tests with ASAN, UBSAN, etc)
- No global state, fully re-entrant
## Arenas
All memory management in upb uses arenas, using the type `upb_Arena`. Arenas are
an alternative to `malloc()` and `free()` that significantly reduces the costs
of memory allocation.
Arenas obtain blocks of memory using some underlying allocator (likely
`malloc()` and `free()`), and satisfy allocations using a simple bump allocator
that walks through each block in linear order. Allocations cannot be freed
individually: it is only possible to free the arena as a whole, which frees all
of the underlying blocks.
Here is an example of using the `upb_Arena` type:
```c
upb_Arena* arena = upb_Arena_New();
// Perform some allocations.
int* x = upb_Arena_Malloc(arena, sizeof(*x));
int* y = upb_Arena_Malloc(arena, sizeof(*y));
// We cannot free `x` and `y` separately, we can only free the arena
// as a whole.
upb_Arena_Free(arena);
```
upb uses arenas for all memory management, and this fact is reflected in the API
for all upb data structures. All upb functions that allocate take a `upb_Arena*`
parameter and perform allocations using that arena rather than calling
`malloc()` or `free()`.
```c
// upb API to create a message.
UPB_API upb_Message* upb_Message_New(const upb_MiniTable* mini_table,
upb_Arena* arena);
void MakeMessage(const upb_MiniTable* mini_table) {
upb_Arena* arena = upb_Arena_New();
// This message is allocated on our arena.
upb_Message* msg = upb_Message_New(mini_table, arena);
// We can free the arena whenever we want, but we cannot free the
// message separately from the arena.
upb_Arena_Free(arena);
// msg is now deleted.
}
```
Arenas are a key part of upb's performance story. Parsing a large protobuf
payload usually involves rapidly creating a series of messages, arrays (repeated
fields), and maps. It is crucial for parsing performance that these allocations
are as fast as possible. Equally important, freeing the tree of messages should
be as fast as possible, and arenas can reduce this cost from `O(n)` to `O(lg
n)`.
### Avoiding Dangling Pointers
Objects allocated on an arena will frequently contain pointers to other
arena-allocated objects. For example, a `upb_Message` will have pointers to
sub-messages that are also arena-allocated.
Unlike unique ownership schemes (such as `unique_ptr<>`), arenas cannot provide
automatic safety from dangling pointers. Instead, upb provides tools to help
bridge between higher-level memory management schemes (GC, refcounting, RAII,
borrow checkers) and arenas.
If there is only one arena, dangling pointers within the arena are impossible,
because all objects are freed at the same time. This is the simplest case. The
user must still be careful not to keep dangling pointers that point at arena
memory after it has been freed, but dangling pointers *between* the arena
objects will be impossible.
But what if there are multiple arenas? If we have a pointer from one arena to
another, how do we ensure that this will not become a dangling pointer?
To help with the multiple arena case, upb provides a primitive called "fuse".
```c
// Fuses the lifetimes of `a` and `b`. None of the blocks from `a` or `b`
// will be freed until both arenas are freed.
UPB_API bool upb_Arena_Fuse(upb_Arena* a, upb_Arena* b);
```
When two arenas are fused together, their lifetimes are irreversibly joined,
such that none of the arena blocks in either arena will be freed until *both*
arenas are freed with `upb_Arena_Free()`. This means that dangling pointers
between the two arenas will no longer be possible.
Fuse is useful when joining two messages from separate arenas (making one a
sub-message of the other). Fuse is a relatively cheap operation, on the order of
150ns, and is very nearly `O(1)` in the number of arenas being fused (the true
complexity is the inverse Ackermann function, which grows extremely slowly).
Each arena does consume some memory, so repeatedly creating and fusing an
additional arena is not free, but the CPU cost of fusing two arenas together is
modest.
### Initial Block and Custom Allocators
`upb_Arena` normally uses `malloc()` and `free()` to allocate and return its
underlying blocks. But this default strategy can be customized to support the
needs of a particular language.
The lowest-level function for creating a `upb_Arena` is:
```c
// Creates an arena from the given initial block (if any -- n may be 0).
// Additional blocks will be allocated from |alloc|. If |alloc| is NULL,
// this is a fixed-size arena and cannot grow.
UPB_API upb_Arena* upb_Arena_Init(void* mem, size_t n, upb_alloc* alloc);
```
The buffer `[mem, n]` will be used as an "initial block", which is used to
satisfy allocations before calling any underlying allocation function. Note that
the `upb_Arena` itself will be allocated from the initial block if possible, so
the amount of memory available for allocation from the arena will be less than
`n`.
The `alloc` parameter specifies a custom memory allocation function which will
be used once the initial block is exhausted. The user can pass `NULL` as the
allocation function, in which case the initial block is the only memory
available in the arena. This can allow upb to be used even in situations where
there is no heap.
It follows that `upb_Arena_Malloc()` is a fallible operation, and all allocating
operations like `upb_Message_New()` should be checked for failure if there is
any possibility that a fixed size arena is in use.
## Schemas
Nearly all operations in upb require that you have a schema. A protobuf schema
is a data structure that contains all of the message, field, enum, etc.
definitions that are specified in a `.proto` file. To create, parse, serialize,
or access a message you must have a schema. For this reason, loading a schema is
generally the first thing you must do when you use upb. [^0]
[^0]: This requirement comes from the protobuf wire format itself, which is a
deep insight about the nature of protobuf (or at least the existing wire
format). Unlike JSON, protobuf cannot be parsed or manipulated in a
schema-less way. This is because the binary wire format does not
distinguish between strings and sub-messages, so a generic parser that is
oblivious to the schema is not possible. If a future version of the wire
format did distinguish between these, it could be possible to have a
schema-agnostic data representation, parser, and serializer.
upb has two main data structures that represent a protobuf schema:
* **MiniTables** are a compact, stripped down version of the schema that
contains only the information necessary for parsing and serializing the
binary wire format.
* **Reflection** contains basically all of the data from a `.proto` file,
including the original names of all messages/fields/etc., and all options.
The table below summarizes the main differences between these two:
| | MiniTables | Reflection |
| ------------------- | ------------------------- | -------------------------- |
| Contains | Field numbers and types | All data in `.proto` file, |
: : only : including names of :
: : : everything :
| Used to parse | binary format | JSON / TextFormat |
| Wire representation | MiniDescriptor | Descriptor |
| Type names | `upb_MiniTable`, | `upb_MessageDef`, |
: : `upb_MiniTableField`, ... : `upb_FieldDef`, ... :
| Registry | `upb_ExtensionRegistry` | `upb_DefPool` |
: : (for extensions) : :
MiniTables are useful if you only need the binary wire format, because they are
much lighter weight than full reflection.
Reflection is useful if you need to parse JSON or TextFormat, or you need access
to options that were specified in the `proto` file. Note that reflection also
includes MiniTables, so if you have reflection, you also have MiniTables
available.
### MiniTables
MiniTables are represented by a set of data structures with names like
`upb_MiniTable` (representing a message), `upb_MiniTableField`,
`upb_MiniTableFile`, etc. Whenever you see one of these types in a function
signature, you know that this particular operation requires a MiniTable. For
example:
```
// Parses the wire format data in the given buffer `[buf, size]` and writes it
// to the message `msg`, which has the type `mt`.
UPB_API upb_DecodeStatus upb_Decode(const char* buf, size_t size,
upb_Message* msg, const upb_MiniTable* mt,
const upb_ExtensionRegistry* extreg,
int options, upb_Arena* arena);
```
The subset of upb that requires only MiniTables can be thought of as "upb lite,"
because both the code size and the runtime memory overhead will be less than
"upb full" (the parts that use reflection).
#### Loading
There are three main ways of loading a MiniTable:
1. **From C generated code:** The upb code generator can emit `.upb.c` files that
contain the MiniTables as global constant variables. When the main program
links against these, the MiniTable will be placed into `.rodata` (or
`.data.rel.ro`) in the binary. The MiniTable can then be obtained from a
generated function. In Blaze/Bazel these files can be generated and linked
using the `upb_proto_library()` rule.
2. **From MiniDescriptors:** The user can build MiniDescriptors into MiniTables
at runtime. MiniDescriptors are a compact upb-specific wire format designed
specially for this purpose. The user can call `upb_MiniTable_Build()` at
runtime to convert MiniDescriptors to MiniTables.
3. **From reflection:** If you have already built reflection data structures
for your type, then you can obtain the `upb_MiniTable` corresponding to a
`upb_MessageDef` using `upb_MessageDef_MiniTable()`.
For languages that are already using reflection, (3) is an obvious choice.
For languages that are avoiding reflection, here is a general guideline for
choosing between (1) and (2): if the language being wrapped participates in the
standard binary linking model on a given platform (in particular, if it is
generally linked using `ld`), then it is better to use (1), which is also known
as "static loading".
Static loading of MiniTables has the benefit of requiring no runtime
initialization[^2], leading to faster startup. Static loading of MiniTables also
facilitates cross-language sharing of proto messages, because sharing generally
requires that both languages are using exactly the same MiniTables.
The main downside of static loading is that it requires the user to generate one
`.upb.c` file per `.proto` and link against the transitive closure of `.upb.c`
files. Blaze and Bazel make this reasonably easy, but for other build systems it
can be more of a challenge.
[^2]: aside from possible pointer relocations performed by the ELF/Mach-O loader
if the library or binary is position-independent
Loading from MiniDescriptors, as in option (2), has the advantage that it does
not require per-message linking of C code. For many language toolchains,
generating and linking some custom C code for every protobuf file or message
type would be a burdensome requirement. MiniDescriptors are a convenient way of
loading MiniTables without needing to cross the FFI boundary outside the core
runtime.
A common pattern when using dynamic loading is to embed strings containing
MiniDescriptors directly into generated code. For example, the generated code in
Dart for a message with only primitive fields currently looks something like:
```dart
const desc = r'$(+),*-#$%&! /10';
_accessor = $pb.instance.registry.newMessageAccessor(desc);
```
The implementation of `newMessageAccesor()` is mainly just a wrapper around
`upb_MiniTable_Build()`, which builds a MiniTable from a MiniDescriptor. In the
code generator, the MiniDescriptor can be obtained from the
`upb_MessageDef_MiniDescriptorEncode()` API; users should never need to encode a
MiniDescriptor manually.
#### Linking
When building MiniTables dynamically, it is the user's responsibility to link
each message to the to the appropriate sub-messages and or enums. Each message
must have its message and closed enum fields linked using
`upb_MiniTable_SetSubMessage()` and `upb_MiniTable_SetSubEnum()`, respectively.
A higher-level function that links all fields at the same time is also
available, as `upb_MiniTable_Link()`. This function pairs well with
`upb_MiniTable_GetSubList()` which can be used in a code generator to get a list
of all the messages and enums which must be passed to `upb_MiniTable_Link()`.
A common pattern is to embed the `link()` calls directly into the generated
code. For example, here is an example from Dart of building a MiniTable that
contains sub-messages and enums:
```dart
const desc = r'$3334';
_accessor = $pb.instance.registry.newMessageAccessor(desc);
_accessor!.link(
[
M2.$_accessor,
M3.$_accessor,
M4.$_accessor,
],
[
E.$_accessor,
],
);
```
In this case, `upb_MiniTable_GetSubList()` was used in the code generator to
discover the 3 sub-message fields and 1 sub-enum field that require linking. At
runtime, these lists of MiniTables are passed to the `link()` function, which
will internally call `upb_MiniTable_Link()`.
Note that in some cases, the application may choose to delay or even skip the
registration of sub-message types, as part of a tree shaking strategy.
When using static MiniTables, a manual link step is not necessary, as linking is
performed automatically by `ld`.
#### Enums
MiniTables primarily carry data about messages, fields, and extensions. However
for closed enums, we must also have a `upb_MiniTableEnum` structure that stores
the set of all numbers that are defined in the enum. This is because closed
enums have the unfortunate behavior of putting unknown enum values into the
unknown field set.
Over time, closed enums will hopefully be phased out via editions, and the
relevance and overhead of `upb_MiniTableEnum` will shrink and eventually
disappear.
### Reflection
Reflection uses types like `upb_MessageDef` and `upb_FieldDef` to represent the
full contents of a `.proto` file at runtime. These types are upb's direct
equivalents of `google::protobuf::Descriptor`, `google::protobuf::FieldDescriptor`, etc. [^1]
[^1]: upb consistently uses `Def` where C++ would use `Descriptor` in type
names. This introduces divergence with C++; the rationale was to conserve
horizontal line length, as `Def` is less than 1/3 the length of
`Descriptor`. This is more relevant in C, where the type name is repeated
in every function, eg. `upb_FieldDef_Name()` vs.
`upb_FieldDescriptor_Name()`.
Whenever you see one of these types in a function signature, you know that the
given operation requires reflection. For example:
```c
// Parses JSON format into a message object, using reflection.
UPB_API bool upb_JsonDecode(const char* buf, size_t size, upb_Message* msg,
const upb_MessageDef* m, const upb_DefPool* symtab,
int options, upb_Arena* arena, upb_Status* status);
```
The part of upb that requires reflection can be thought of as "upb full." These
parts of the library cannot be used if a given application has only loaded
MiniTables. There is no way to convert a MiniTable into reflection.
The `upb_DefPool` type is the top-level container that builds and owns some set
of defs. This type is a close analogue of `google::protobuf::DescriptorPool` in C++. The
user must always ensure that the `upb_DefPool` outlives any def objects that it
owns.
#### Loading
As with MiniTable loading, we have multiple options for how to load full
reflection:
1. **From C generated code**: The upb code generator can create `foo.upbdefs.c`
files that embed the descriptors and exports generated C functions for
adding them to a user-provided `upb_DefPool`.
2. **From descriptors**: The user can make manual calls to
`upb_DefPool_AddFile()`, using descriptors obtained at runtime. Defs for
individual messages can then be obtained using
`upb_DefPool_FindMessageByName()`.
Unlike MiniTables, loading from generated code requires runtime initialization,
as reflection data structures like `upb_MessageDef` are not capable of being
emitted directly into `.rodata` like `upb_MiniTable` is. Instead, the generated
code embeds serialized descriptor protos into `.rodata` which are then built
into heap objects at runtime.
From this you might conclude that option (1) is nothing but a convenience
wrapper around option (2), but that is not quite correct either. Option (1)
*does* link against the static `.upb.c` structures for the MiniTables, whereas
option (2) will build the MiniTables from scratch on the heap. So option (1)
will use marginally less CPU and RAM when the descriptors are loaded into a
`upb_DefPool`. More importantly, the resulting descriptors will be capable of
reflecting over any messages built from the generated `.upb.c` MiniTables,
whereas descriptors built using option (2) will have distinct MiniTables that
cannot reflect over messages that use the generated MiniTables.
A common pattern for dynamic languages like PHP, Ruby, or Python, is to use
option (2) with descriptors that are embedded into the generated code. For
example, the generated code in Python currently looks something like:
```python
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf.internal import builder as _builder
_desc = b'\n\x1aprotoc_explorer/main.proto\x12\x03pkg'
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(_desc)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google3.protoc_explorer.main_pb2', _globals)
```
The `AddSerializedFile()` API above is mainly just a thin wrapper around
`upb_DefPool_AddFile()`.

@ -0,0 +1,73 @@
#!/usr/bin/env python3
# Protocol Buffers - Google's data interchange format
# Copyright 2023 Google LLC. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google LLC nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import subprocess
import sys
import shutil
import os
if len(sys.argv) < 2:
print("Must pass a filename argument")
sys.exit(1)
in_filename = sys.argv[1]
out_filename = in_filename.replace(".in.md", ".md")
out_dir = in_filename.replace(".in.md", "")
if in_filename == out_filename:
print("File must end in .in.md")
sys.exit(1)
if os.path.isdir(out_dir):
shutil.rmtree(out_dir)
os.mkdir(out_dir)
file_num = 1
with open(out_filename, "wb") as out_file, open(in_filename, "rb") as in_file:
for line in in_file:
if line.startswith(b"```dot"):
dot_lines = []
while True:
dot_line = next(in_file)
if dot_line == b"```\n":
break
dot_lines.append(dot_line)
dot_input = b"".join(dot_lines)
svg_filename = out_dir + "/" + str(file_num) + ".svg"
svg = subprocess.check_output(['dot', '-Tsvg', '-o', svg_filename], input=dot_input)
out_file.write(b"<div align=center>\n")
out_file.write(b"<img src='%s'/>\n" % (svg_filename.encode('utf-8')))
out_file.write(b"</div>\n")
file_num += 1
else:
out_file.write(line)

@ -0,0 +1,65 @@
# C style guide
<!--*
# Document freshness: For more information, see go/fresh-source.
freshness: { owner: 'haberman' reviewed: '2022-05-08' }
*-->
Since upb is written in pure C, we supplement the
[Google C++ style guide](https://google.github.io/styleguide/cppguide.html) with
some C-specific guidance.
Everything written here is intended to follow the spirit of the C++ style guide.
upb is currently inconsistent about following these conventions. It is intended
that all code will be updated to match these guidelines. The priority is
converting public interfaces as these are more difficult to change later.
## Naming
### Functions and Types
C does not have namespaces. Anywhere you would normally use a namespace
separator (`::`) in C++, we use an underscore (`_`) in C:
```c++
// C equivalent for upb::Arena::New()
upb_Arena* upb_Arena_New();
```
Since we rely on `_` to be our namespace separator, we never use it to merely
separate words in function or type names:
```c++
// BAD: this would be interpreted as upb::FieldDef::has::default().
bool upb_FieldDef_has_default(const upb_FieldDef* f);
// GOOD: this is equivalent to upb::FieldDef::HasDefault().
bool upb_FieldDef_HasDefault(const upb_FieldDef* f);
```
For multi-word namespaces, we use `PascalCase`:
```c++
// `PyUpb` is the namespace.
PyObject* PyUpb_CMessage_GetAttr(PyObject* _self, PyObject* attr);
```
### Private Functions and Members
Since we do not have `private` in C++, we use a leading underscore convention
to mark internal functions and variables that should only be accessed from
upb:
```c++
// Internal-only function.
int64_t _upb_Int64_FromLL();
// Internal-only members. Underscore prefixes are only necessary when the
// structure is defined in a header file.
typedef struct {
const int32_t* _values; // List of values <0 or >63
uint64_t _mask; // Bits are set for acceptable value 0 <= x < 64
int _value_count;
} upb_MiniTableEnum;
```

@ -0,0 +1,261 @@
<!--*
# Document freshness: For more information, see go/fresh-source.
freshness: { owner: 'haberman' reviewed: '2023-02-24' }
*-->
# upb vs. C++ Protobuf Design
[upb](https://github.com/protocolbuffers/upb) is a small C protobuf library.
While some of the design follows in the footsteps of the C++ Protobuf Library,
upb departs from C++'s design in several key ways. This document compares
and contrasts the two libraries on several design points.
## Design Goals
Before we begin, it is worth calling out that upb and C++ have different design
goals, and this motivates some of the differences we will see.
C++ protobuf is a user-level library: it is designed to be used directly by C++
applications. These applications will expect a full-featured C++ API surface
that uses C++ idioms. The C++ library is also willing to add features to
increase server performance, even if these features would add size or complexity
to the library. Because C++ protobuf is a user-level library, API stability is
of utmost importance: breaking API changes are rare and carefully managed when
they do occur. The focus on C++ also means that ABI compatibility with C is not
a priority.
upb, on the other hand, is designed primarily to be wrapped by other languages.
It is a C protobuf kernel that forms the basis on which a user-level protobuf
library can be built. This means we prefer to keep the API surface as small and
orthogonal as possible. While upb supports all protobuf features required for
full conformance, upb prioritizes simplicity and small code size, and avoids
adding features like lazy fields that can accelerate some use cases but at great
cost in terms of complexity. As upb is not aimed directly at users, there is
much more freedom to make API-breaking changes when necessary, which helps the
core to stay small and simple. We want to be compatible with all FFI
interfaces, so C ABI compatibility is a must.
Despite these differences, C++ protos and upb offer [roughly the same core set
of features](https://github.com/protocolbuffers/upb#features).
## Arenas
upb and C++ protos both offer arena allocation, but there are some key
differences.
### C++
As a matter of history, when C++ protos were open-sourced in 2008, they did not
support arenas. Originally there was only unique ownership, whereby each
message uniquely owns all child messages and will free them when the parent is
freed.
Arena allocation was added as a feature in 2014 as a way of dramatically
reducing allocation and (especially) deallocation costs. But the library was
not at liberty to remove the unique ownership model, because it would break far
too many users. As a result, C++ has supported a **hybrid allocation model**
ever since, allowing users to allocate messages either directly from the
stack/heap or from an arena. The library attempts to ensure that there are
no dangling pointers by performing automatic copies in some cases (for example
`a->set_allocated_b(b)`, where `a` and `b` are on different arenas).
C++'s arena object itself `google::protobuf::Arena` is **thread-safe** by
design, which allows users to allocate from multiple threads simultaneously
without external synchronization. The user can supply an initial block of
memory to the arena, and can choose some parameters to control the arena block
size. The user can also supply block alloc/dealloc functions, but the alloc
function is expected to always return some memory. The C++ library in general
does not attempt to handle out of memory conditions.
### upb
upb uses **arena allocation exclusively**. All messages must be allocated from
an arena, and can only be freed by freeing the arena. It is entirely the user's
responsibility to ensure that there are no dangling pointers: when a user sets a
message field, this will always trivially overwrite the pointer and will never
perform an implicit copy.
upb's `upb::Arena` is **thread-compatible**, which means it cannot be used
concurrently without synchronization. The arena can be seeded with an initial
block of memory, but it does not explicitly support any parameters for choosing
block size. It supports a custom alloc/dealloc function, and this function is
allowed to return `NULL` if no dynamic memory is available. This allows upb
arenas to have a max/fixed size, and makes it possible in theory to write code
that is tolerant to out-of-memory errors.
upb's arena also supports a novel operation known as **fuse**, which joins two
arenas together into a single lifetime. Though both arenas must still be freed
separately, none of the memory will actually be freed until *both* arenas have
been freed. This is useful for avoiding dangling pointers when reparenting a
message with one that may be on a different arena.
### Comparison
**hybrid allocation vs. arena-only**
* The C++ hybrid allocation model introduces a great deal of complexity and
unpredictability into the library. upb benefits from having a much simpler
and more predictable design.
* Some of the complexity in C++'s hybrid model arises from the fact that arenas
were added after the fact. Designing for a hybrid model from the outset
would likely yield a simpler result.
* Unique ownership does support some usage patterns that arenas cannot directly
accommodate. For example, you can reparent a message and the child will precisely
follow the lifetime of its new parent. An arena would require you to either
perform a deep copy or extend the lifetime.
**thread-compatible vs. thread-safe arena**
* A thread-safe arena (as in C++) is safer and easier to use. A thread-compatible
arena requires that the user prove that the arena cannot be used concurrently.
* [Thread Sanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual)
is far more accessible than it was in 2014 (when C++ introduced a thread-safe
arena). We now have more tools at our disposal to ensure that we do not trigger
data races in a thread-compatible arena like upb.
* Thread-compatible arenas are more performant.
* Thread-compatible arenas have a far simpler implementation. The C++ thread-safe
arena relies on thread-local variables, which introduce complications on some
platforms. It also requires far more subtle reasoning for correctness and
performance.
**fuse vs. no fuse**
* The `upb_Arena_Fuse()` operation is a key part of how upb supports reparenting
of messages when the parent may be on a different arena. Without this, upb has
no way of supporting `foo.bar = bar` in dynamic languages without performing a
deep copy.
* A downside of `upb_Arena_Fuse()` is that passing an arena to a function can allow
that function to extend the lifetime of the arena in potentially
unpredictable ways. This can be prevented if necessary, as fuse can fail, eg. if
one arena has an initial block. But this adds some complexity by requiring callers
to handle the case where fuse fails.
## Code Generation vs. Tables
The C++ protobuf library has always been built around code generation, while upb
generates only tables. In other words, `foo.pb.cc` files contain functions,
whereas `foo.upb.c` files emit only data structures.
### C++
C++ generated code emits a large number of functions into `foo.pb.cc` files.
An incomplete list:
* `FooMsg::FooMsg()` (constructor): initializes all fields to their default value.
* `FooMsg::~FooMsg()` (destructor): frees any present child messages.
* `FooMsg::Clear()`: clears all fields back to their default/empty value.
* `FooMsg::_InternalParse()`: generated code for parsing a message.
* `FooMsg::_InternalSerialize()`: generated code for serializing a message.
* `FooMsg::ByteSizeLong()`: calculates serialized size, as a first pass before serializing.
* `FooMsg::MergeFrom()`: copies/appends present fields from another message.
* `FooMsg::IsInitialized()`: checks whether required fields are set.
This code lives in the `.text` section and contains function calls to the generated
classes for child messages.
### upb
upb does not generate any code into `foo.upb.c` files, only data structures. upb uses a
compact data table known as a *mini table* to represent the schema and all fields.
upb uses mini tables to perform all of the operations that would traditionally be done
with generated code. Revisiting the list from the previous section:
* `FooMsg::FooMsg()` (constructor): upb instead initializes all messages with `memset(msg, 0, size)`.
Non-zero defaults are injected in the accessors.
* `FooMsg::~FooMsg()` (destructor): upb messages are freed by freeing the arena.
* `FooMsg::Clear()`: can be performed with `memset(msg, 0, size)`.
* `FooMsg::_InternalParse()`: upb's parser uses mini tables as data, instead of generating code.
* `FooMsg::_InternalSerialize()`: upb's serializer also uses mini-tables instead of generated code.
* `FooMsg::ByteSizeLong()`: upb performs serialization in reverse so that an initial pass is not required.
* `FooMsg::MergeFrom()`: upb supports this via serialize+parse from the other message.
* `FooMsg::IsInitialized()`: upb's encoder and decoder have special flags to check for required fields.
A util library `upb/util/required_fields.h` handles the corner cases.
### Comparison
If we compare compiled code size, upb is far smaller. Here is a comparison of the code
size of a trivial binary that does nothing but a parse and serialize of `descriptor.proto`.
This means we are seeing both the overhead of the core library itself as well as the
generated code (or table) for `descriptor.proto`. (For extra clarity we should break this
down by generated code vs core library in the future).
| Library | `.text` | `.data` | `.bss` |
|------------ |---------|---------|--------|
| upb | 26Ki | 0.6Ki | 0.01Ki |
| C++ (lite) | 187Ki | 2.8Ki | 1.25Ki |
| C++ (code size) | 904Ki | 6.1Ki | 1.88Ki |
| C++ (full) | 983Ki | 6.1Ki | 1.88Ki |
"C++ (code size)" refers to protos compiled with `optimize_for = CODE_SIZE`, a mode
in which generated code contains reflection only, in an attempt to make the
generated code size smaller (however it requires the full runtime instead
of the lite runtime).
## Bifurcated vs. Optional Reflection
upb and C++ protos both offer reflection without making it mandatory. However
the models for enabling/disabling reflection are very different.
### C++
C++ messages offer full reflection by default. Messages in C++ generally
derive from `Message`, and the base class provides a member function
`Reflection* Message::GetReflection()` which returns the reflection object.
It follows that any message deriving from `Message` will always have reflection
linked into the binary, whether or not the reflection object is ever used.
Because `GetReflection()` is a function on the base class, it is not possible
to statically determine if a given message's reflection is used:
```c++
Reflection* GetReflection(const Message& message) {
// Can refer to any message in the whole binary.
return message.GetReflection();
}
```
The C++ library does provide a way of omitting reflection: `MessageLite`. We can
cause a message to be lite in two different ways:
* `optimize_for = LITE_RUNTIME` in a `.proto` file will cause all messages in that
file to be lite.
* `lite` as a codegen param: this will force all messages to lite, even if the
`.proto` file does not have `optimize_for = LITE_RUNTIME`.
A lite message will derive from `MessageLite` instead of `Message`. Since
`MessageLite` has no `GetReflection()` function, this means no reflection is
available, so we can avoid taking the code size hit.
### upb
upb does not have the `Message` vs. `MessageLite` bifurcation. There is only one
kind of message type `upb_Message`, which means there is no need to configure in
a `.proto` file which messages will need reflection and which will not.
Every message has the *option* to link in reflection from a separate `foo.upbdefs.o`
file, without needing to change the message itself in any way.
upb does not provide the equivalent of `Message::GetReflection()`: there is no
facility for retrieving the reflection of a message whose type is not known statically.
It would be possible to layer such a facility on top of the upb core, though this
would probably require some kind of code generation.
### Comparison
* Most messages in C++ will not bother to declare themselves as "lite". This means
that many C++ messages will link in reflection even when it is never used, bloating
binaries unnecessarily.
* `optimize_for = LITE_RUNTIME` is difficult to use in practice, because it prevents
any non-lite protos from `import`ing that file.
* Forcing all protos to lite via a codegen parameter (for example, when building for
mobile) is more practical than `optimize_for = LITE_RUNTIME`. But this will break
the compile for any code that tries to upcast to `Message`, or tries to use a
non-lite method.
* The one major advantage of the C++ model is that it can support `msg.DebugString()`
on a type-erased proto. For upb you have to explicitly pass the `upb_MessageDef*`
separately if you want to perform an operation like printing a proto to text format.
## Explicit Registration vs. Globals
TODO

@ -0,0 +1,444 @@
<!---
This document contains embedded graphviz diagrams inside ```dot blocks.
To convert it to rendered form using render.py:
$ ./render.py wrapping-upb.in.md
You can also live-preview this document with all diagrams using Markdown Preview Enhanced
in Visual Studio Code:
https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced
--->
# Building a protobuf library on upb
This is a guide for creating a new protobuf implementation based on upb. It
starts from the beginning and walks you through the process, highlighting
some important design choices you will need to make.
## Overview
A protobuf implementation consists of two main pieces:
1. a code generator, run at compile time, to turn `.proto` files into source
files in your language (we will call this "zlang", assuming an extension of ".z").
2. a runtime component, which implements the wire format and provides the data
structures for representing protobuf data and metadata.
<br/>
```dot {align="center"}
digraph {
rankdir=LR;
newrank=true;
node [style="rounded,filled" shape=box]
"foo.proto" -> protoc;
"foo.proto" [shape=folder];
protoc [fillcolor=lightgrey];
protoc -> "protoc-gen-zlang";
"protoc-gen-zlang" -> "foo.z";
"protoc-gen-zlang" [fillcolor=palegreen3];
"foo.z" [shape=folder];
labelloc="b";
label="Compile Time";
}
```
<br/>
```dot {align="center"}
digraph {
newrank=true;
node [style="rounded,filled" shape=box fillcolor=lightgrey]
"foo.z" -> "zlang/upb glue (FFI)";
"zlang/upb glue (FFI)" -> "upb (C)";
"zlang/upb glue (FFI)" [fillcolor=palegreen3];
labelloc="b";
label="Runtime";
}
```
The parts in green are what you will need to implement.
Note that your code generator (`protoc-gen-zlang`) does *not* need to generate
any C code (eg. `foo.c`). While upb itself is written in C, upb's parsers and
serializers are fully table-driven, which means there is never any need or even
benefit to generating C code for each proto. upb is capable of full-speed
parsing even when schema data is loaded at runtime from strings embedded into
`foo.z`. This is a key benefit of upb compared with C++ protos, which have
traditionally relied on generated parsers in `foo.pb.cc` files to achieve full
parsing speed, and suffered a ~10x speed penalty in the parser when the schema
data was loaded at runtime.
## Prerequisites
There are a few things that the language runtime must provide in order to wrap
upb.
1. **FFI**: To wrap upb, your language must be able to call into a C API
through a Foreign Function Interface (FFI). Most languages support FFI in
some form, either through "native extensions" (in which you write some C
code to implement new methods in your language) or through a direct FFI (in
which you can call into regular C functions directly from your language
using a special library).
2. **Finalizers, Destructors, or Cleaners**: The runtime must provide
finalizers or destructors of some sort. There must be a way of triggering a
call to a C function when the language garbage collects or otherwise
destroys an object. We don't care much whether it is a finalizer, a
destructor, or a cleaner, as long as it gets called eventually when the
object is destroyed. upb allocates memory in C space, and a finalizer is our
only way of making sure that memory is freed and does not leak.
3. **HashMap with weak values**: (optional) This is not a strong requirement,
but it is sometimes helpful to have a global hashmap with weak values to act
as a `upb_msg* -> wrapper` object cache. We want the values to be weak (not
the keys). There is some question about whether we want to continue to use
this pattern going forward.
## Reflection vs. MiniTables
The first key design decision you will need to make is whether your generated
code will access message data via reflection or minitables. Generally more
dynamic languages will want to use reflection and more static languages will
want to use minitables.
### Reflection
Reflection-based data access makes the most sense in highly dynamic language
interpreters, where method dispatch is generally resolved via strings and hash
table lookups.
In such languages, you can often implement a special method like `__getattr__`
(Python) or `method_missing` (Ruby) that receives the method name as a string.
Using upb's reflection, you can look up a field name using the method name,
thereby using a hash table belonging to upb instead of one provided by the
language.
```python
class FooMessage:
# Written in Python for illustration, but in practice we will want to
# implement this in C for speed.
def __getattr__(self, name):
field = FooMessage.descriptor.fields_by_name[name]
return field.get_value(self)
```
Using this design, we only need to attach a single `__getattr__` method to each
message class, instead of defining a getter/setter for each field. In this way
we can avoid duplicating hash tables between upb and the language interpreter,
reducing memory usage.
Reflection-based access requires loading full reflection at runtime. Your
generated code will need to embed serialized descriptors (ie. a serialized
message of `descriptor.proto`), which has some amount of size overhead and
exposes all message/field names to the binary. It also forces a hash table
lookup in the critical path of field access. If method calls in your language
already have this overhead, then this is no added burden, but for statically
dispatched languages it would cause extra overhead.
If we take this path to its logical conclusion, all class creation can be
performed fully dynamically, using only a binary descriptor as input. The
"generated code" becomes little more than an embedded descriptor plus a
library call to load it. Python has recently gone down this path. Generated
code now looks something like this:
```python
# main_pb2.py
from google3.net.proto2.python.internal import builder as _builder
from google3.net.proto2.python.public import descriptor_pool as _descriptor_pool
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile("<...>")
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google3.main_pb2', globals())
```
This is all the runtime needs to create all of the classes for messages defined
in that serialized descriptor. This code has no pretense of readability, but
a separate `.pyi` stub file provides a fully expanded and readable list of all
methods a user can expect to be available:
```python
# main_pb2.pyi
from google3.net.proto2.python.public import descriptor as _descriptor
from google3.net.proto2.python.public import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional
DESCRIPTOR: _descriptor.FileDescriptor
class MyMessage(_message.Message):
__slots__ = ["my_field"]
MY_FIELD_FIELD_NUMBER: _ClassVar[int]
my_field: str
def __init__(self, my_field: _Optional[str] = ...) -> None: ...
```
To use reflection-based access:
1. Load and access descriptor data using the interfaces in upb/reflection/def.h.
2. Access message data using the interfaces in upb/reflection/message.h.
### MiniTables
MiniTables are a "lite" schema representation that are much smaller than
reflection. MiniTables omit names, options, and almost everything else from the
`.proto` file, retaining only enough information to parse and serialize binary
format.
MiniTables can be loaded into upb through *MiniDescriptors*. MiniDescriptors are
a byte-oriented format that can be embedded into your generated code and passed
to upb to construct MiniTables. MiniDescriptors only use printable characters,
and therefore do not require escaping when embedding them into generated code
strings. Overall the size savings of MiniDescriptors are ~60x compared with
regular descriptors.
MiniTables and MiniDescriptors are a natural choice for compiled languages that
resolve method calls at compile time. For languages that are sometimes compiled
and sometimes interpreted, there might not be an obvious choice. When a method
call is statically bound, we want to remove as much overhead as possible,
especially from accessors. In the extreme case, we can use unsafe APIs to read
raw memory at a known offset:
```java
// Example of a maximally-optimized generated accessor.
class FooMessage {
public long getBarField() {
// Using Unsafe should give us performance that is comparable to a
// native member access.
//
// The constant "24" is obtained from upb at compile time.
sun.misc.Unsafe.getLong(this.ptr, 24);
}
}
```
This design is very low-level, and tightly couples the generated code to one
specific version of the schema and compiler. A slower but safer version would
look up a field by field number:
```java
// Example of a more loosely-coupled accessor.
class FooMessage {
public long getBarField() {
// The constant "2" is the field number. Internally this will look
// up the number "2" in the MiniTable and use that to read the value
// from the message.
upb.glue.getLong(this.ptr, 2);
}
}
```
One downside of MiniTables is that they cannot support parsing or serializing
to JSON or TextFormat, because they do not know the field names. It should be
possible to generate reflection data "on the side", into separate generated
code files, so that reflection is only pulled in if it is being used. However
APIs to do this do not exist yet.
To use MiniTable-based access:
1. Load and access MiniDescriptors data using the interfaces in upb/mini_descriptor/decode.h.
2. Access message data using the interfaces in upb/message/accessors.h.
## Memory Management
One of the core design challenges when wrapping upb is memory management. Every
language runtime will have some memory management system, whether it is
garbage collection, reference counting, manual memory management, or some hybrid
of these. upb is written in C and uses arenas for memory management, but upb is
designed to integrate with a wide variety of memory management schemes, and it
provides a number of tools for making this integration as smooth as possible.
### Arenas
upb defines data structures in C to represent messages, arrays (repeated
fields), and maps. A protobuf message is a hierarchical tree of these objects.
For example, a relatively simple protobuf tree might look something like this:
```dot {align="center"}
digraph G {
rankdir=LR;
newrank=true;
node [style="rounded,filled" shape=box colorscheme=accent8 fillcolor=1, ordering=out]
upb_msg -> upb_msg2;
upb_msg -> upb_array;
upb_msg [label="upb Message" fillcolor=1]
upb_msg2 [label="upb Message"];
upb_array [label="upb Array"]
}
```
All upb objects are allocated from an arena. An arena lets you allocate objects
individually, but you cannot free individual objects; you can only free the arena
as a whole. When the arena is freed, all of the individual objects allocated
from that arena are freed together.
```dot {align="center"}
digraph G {
rankdir=LR;
newrank=true;
subgraph cluster_0 {
label = "upb Arena"
graph[style="rounded,filled" fillcolor=gray]
node [style="rounded,filled" shape=box colorscheme=accent8 fillcolor=1, ordering=out]
upb_msg -> upb_array;
upb_msg -> upb_msg2;
upb_msg [label="upb Message" fillcolor=1]
upb_msg2 [label="upb Message"];
upb_array [label="upb Array"];
}
}
```
In simple cases, the entire tree of objects will all live in a single arena.
This has the nice property that there cannot be any dangling pointers between
objects, since all objects are freed at the same time.
However upb allows you to create links between any two objects, whether or
not they are in the same arena. The library does not know or care what arenas
the objects are in when you create links between them.
```dot {align="center"}
digraph G {
rankdir=LR;
newrank=true;
subgraph cluster_0 {
label = "upb Arena 1"
graph[style="rounded,filled" fillcolor=gray]
node [style="rounded,filled" shape=box colorscheme=accent8 fillcolor=1, ordering=out]
upb_msg -> upb_array;
upb_msg -> upb_msg2;
upb_msg [label="upb Message 1" fillcolor=1]
upb_msg2 [label="upb Message 2"];
upb_array [label="upb Array"];
}
subgraph cluster_1 {
label = "upb Arena 2"
graph[style="rounded,filled" fillcolor=gray]
node [style="rounded,filled" shape=box colorscheme=accent8 fillcolor=1]
upb_msg3;
}
upb_msg2 -> upb_msg3;
upb_msg3 [label="upb Message 3"];
}
```
When objects are on separate arenas, it is the user's responsibility to ensure
that there are no dangling pointers. In the example above, this means Arena 2
must outlive Message 1 and Message 2.
### Integrating GC with upb
In languages with automatic memory management, the goal is to handle all of the
arenas behind the scenes, so that the user does not have to manage them manually
or even know that they exist.
We can achieve this goal if we set up the object graph in a particular way. The
general strategy is to create wrapper objects around all of the C objects,
including the arena. Our key goal is to make sure the arena wrapper is not
GC'd until all of the C objects in that arena have become unreachable.
For this example, we will assume we are wrapping upb in Python:
```dot {align="center"}
digraph G {
rankdir=LR;
newrank=true;
compound=true;
subgraph cluster_1 {
label = "upb Arena"
graph[style="rounded,filled" fillcolor=gray]
node [style="rounded,filled" shape=box colorscheme=accent8 fillcolor=1, ordering=out]
upb_msg -> upb_array [style=dashed];
upb_msg -> upb_msg2 [style=dashed];
upb_msg [label="upb Message" fillcolor=1]
upb_msg2 [label="upb Message"];
upb_array [label="upb Array"]
dummy [style=invis]
}
subgraph cluster_python {
node [style="rounded,filled" shape=box colorscheme=accent8 fillcolor=2]
peripheries=0
py_upb_msg [label="Python Message"];
py_upb_msg2 [label="Python Message"];
py_upb_arena [label="Python Arena"];
}
py_upb_msg -> upb_msg [style=dashed];
py_upb_msg2->upb_msg2 [style=dashed];
py_upb_msg2 -> py_upb_arena [color=springgreen4];
py_upb_msg -> py_upb_arena [color=springgreen4];
py_upb_arena -> dummy [lhead=cluster_1, color=red];
{
rank=same;
upb_msg;
py_upb_msg;
}
{
rank=same;
upb_array;
upb_msg2;
py_upb_msg2;
}
{ rank=same;
dummy;
py_upb_arena;
}
dummy->upb_array [style=invis];
dummy->upb_msg2 [style=invis];
subgraph cluster_01 {
node [shape=plaintext]
peripheries=0
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
<tr><td align="right" port="i1">raw ptr</td></tr>
<tr><td align="right" port="i2">unique ptr</td></tr>
<tr><td align="right" port="i3">shared (GC) ptr</td></tr>
</table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
<tr><td port="i1">&nbsp;</td></tr>
<tr><td port="i2">&nbsp;</td></tr>
<tr><td port="i3">&nbsp;</td></tr>
</table>>]
key:i1:e -> key2:i1:w [style=dashed]
key:i2:e -> key2:i2:w [color=red]
key:i3:e -> key2:i3:w [color=springgreen4]
}
key2:i1:w -> upb_msg [style=invis];
{
rank=same;
key;
upb_msg;
}
}
```
In this example we have three different kinds of pointers:
* **raw ptr**: This is a pointer that carries no ownership.
* **unique ptr**: This is a pointer has *unique ownership* of the target. The owner
will free the target in its destructor (or finalizer, or cleaner). There can
only be a single unique pointer to a given object.
* **shared (GC) ptr**: This is a pointer that has *shared ownership* of the
target. Many objects can point to the target, and the target will be deleted
only when all such references are gone. In a runtime with automatic memory
management (GC), this is a reference that participates in GC. In Python such
references use reference counting, but in other VMs they may use mark and
sweep or some other form of GC instead.
The Python Message wrappers have only raw pointers to the underlying message,
but they contain a shared pointer to the arena that will ensure that the raw
pointer remains valid. Only when all message wrapper objects are destroyed
will the Python Arena become unreachable, and the upb arena ultimately freed.
### Links between arenas with "Fuse"
The design given above works well for objects that live in a single arena. But
what if a user wants to create a link between two objects in different arenas?
TODO
## UTF-8 vs. UTF-16
TODO
## Object Cache
TODO

@ -0,0 +1,130 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load(
"//bazel:build_defs.bzl",
"UPB_DEFAULT_COPTS",
"UPB_DEFAULT_CPPOPTS",
)
load(
"//lua:lua_proto_library.bzl",
"lua_proto_library",
)
licenses(["notice"])
cc_library(
name = "lupb",
srcs = [
"def.c",
"msg.c",
"upb.c",
],
hdrs = [
"upb.h",
],
copts = UPB_DEFAULT_COPTS,
visibility = ["//visibility:public"],
deps = [
"//:json",
"//:message",
"//:reflection",
"//:text",
"@lua//:liblua",
],
)
cc_binary(
name = "protoc-gen-lua",
srcs = ["upbc.cc"],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
"@com_google_absl//absl/strings",
"@com_google_protobuf//src/google/protobuf/compiler:code_generator",
],
)
exports_files(["upb.lua"])
cc_test(
name = "test_lua",
srcs = ["main.c"],
args = ["$(location :test_upb.lua)"],
copts = UPB_DEFAULT_COPTS,
data = [
"test_upb.lua",
":descriptor_proto_lua",
":empty_proto_lua",
":test_messages_proto2_proto_lua",
":test_messages_proto3_proto_lua",
":test_proto_lua",
"//:third_party/lunit/console.lua",
"//:third_party/lunit/lunit.lua",
"//lua:upb.lua",
"@com_google_protobuf//:descriptor_proto",
"@com_google_protobuf//conformance:conformance_proto",
],
linkstatic = 1,
deps = [
"//lua:lupb",
"@lua//:liblua",
],
)
proto_library(
name = "test_proto",
testonly = 1,
srcs = ["test.proto"],
deps = ["@com_google_protobuf//:timestamp_proto"],
)
lua_proto_library(
name = "test_proto_lua",
testonly = 1,
deps = [":test_proto"],
)
lua_proto_library(
name = "descriptor_proto_lua",
deps = ["@com_google_protobuf//:descriptor_proto"],
)
lua_proto_library(
name = "empty_proto_lua",
deps = ["@com_google_protobuf//:empty_proto"],
)
lua_proto_library(
name = "test_messages_proto3_proto_lua",
testonly = 1,
deps = ["@com_google_protobuf//src/google/protobuf:test_messages_proto3_proto"],
)
lua_proto_library(
name = "test_messages_proto2_proto_lua",
testonly = 1,
deps = ["@com_google_protobuf//src/google/protobuf:test_messages_proto2_proto"],
)

@ -0,0 +1,8 @@
# upb Lua bindings
These are some bare-bones upb bindings for Lua.
These bindings exist primarily for experimentation and testing.
They are incomplete and are not really intended for use in any application.
This is by no means a complete or supported protobuf library, and in fact
we don't even claim it to be functional.

@ -0,0 +1,943 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "upb/reflection/def.h"
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "lauxlib.h"
#include "lua/upb.h"
#include "upb/reflection/message.h"
#define LUPB_ENUMDEF "lupb.enumdef"
#define LUPB_ENUMVALDEF "lupb.enumvaldef"
#define LUPB_FIELDDEF "lupb.fielddef"
#define LUPB_FILEDEF "lupb.filedef"
#define LUPB_MSGDEF "lupb.msgdef"
#define LUPB_ONEOFDEF "lupb.oneof"
#define LUPB_SYMTAB "lupb.defpool"
#define LUPB_OBJCACHE "lupb.objcache"
static void lupb_DefPool_pushwrapper(lua_State* L, int narg, const void* def,
const char* type);
/* lupb_wrapper ***************************************************************/
/* Wrappers around upb def objects. The userval contains a reference to the
* defpool. */
#define LUPB_SYMTAB_INDEX 1
typedef struct {
const void* def; /* upb_MessageDef, upb_EnumDef, upb_OneofDef, etc. */
} lupb_wrapper;
static const void* lupb_wrapper_check(lua_State* L, int narg,
const char* type) {
lupb_wrapper* w = luaL_checkudata(L, narg, type);
return w->def;
}
static void lupb_wrapper_pushdefpool(lua_State* L, int narg) {
lua_getiuservalue(L, narg, LUPB_SYMTAB_INDEX);
}
/* lupb_wrapper_pushwrapper()
*
* For a given def wrapper at index |narg|, pushes a wrapper for the given |def|
* and the given |type|. The new wrapper will be part of the same defpool. */
static void lupb_wrapper_pushwrapper(lua_State* L, int narg, const void* def,
const char* type) {
lupb_wrapper_pushdefpool(L, narg);
lupb_DefPool_pushwrapper(L, -1, def, type);
lua_replace(L, -2); /* Remove defpool from stack. */
}
/* lupb_MessageDef_pushsubmsgdef()
*
* Pops the msgdef wrapper at the top of the stack and replaces it with a msgdef
* wrapper for field |f| of this msgdef (submsg may not be direct, for example
* it may be the submessage of the map value).
*/
void lupb_MessageDef_pushsubmsgdef(lua_State* L, const upb_FieldDef* f) {
const upb_MessageDef* m = upb_FieldDef_MessageSubDef(f);
assert(m);
lupb_wrapper_pushwrapper(L, -1, m, LUPB_MSGDEF);
lua_replace(L, -2); /* Replace msgdef with submsgdef. */
}
/* lupb_FieldDef **************************************************************/
const upb_FieldDef* lupb_FieldDef_check(lua_State* L, int narg) {
return lupb_wrapper_check(L, narg, LUPB_FIELDDEF);
}
static int lupb_FieldDef_ContainingOneof(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
const upb_OneofDef* o = upb_FieldDef_ContainingOneof(f);
lupb_wrapper_pushwrapper(L, 1, o, LUPB_ONEOFDEF);
return 1;
}
static int lupb_FieldDef_ContainingType(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
const upb_MessageDef* m = upb_FieldDef_ContainingType(f);
lupb_wrapper_pushwrapper(L, 1, m, LUPB_MSGDEF);
return 1;
}
static int lupb_FieldDef_Default(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
upb_CType type = upb_FieldDef_CType(f);
if (type == kUpb_CType_Message) {
return luaL_error(L, "Message fields do not have explicit defaults.");
}
lupb_pushmsgval(L, 0, type, upb_FieldDef_Default(f));
return 1;
}
static int lupb_FieldDef_Type(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushnumber(L, upb_FieldDef_Type(f));
return 1;
}
static int lupb_FieldDef_HasSubDef(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushboolean(L, upb_FieldDef_HasSubDef(f));
return 1;
}
static int lupb_FieldDef_Index(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushinteger(L, upb_FieldDef_Index(f));
return 1;
}
static int lupb_FieldDef_IsExtension(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushboolean(L, upb_FieldDef_IsExtension(f));
return 1;
}
static int lupb_FieldDef_Label(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushinteger(L, upb_FieldDef_Label(f));
return 1;
}
static int lupb_FieldDef_Name(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushstring(L, upb_FieldDef_Name(f));
return 1;
}
static int lupb_FieldDef_Number(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
int32_t num = upb_FieldDef_Number(f);
if (num) {
lua_pushinteger(L, num);
} else {
lua_pushnil(L);
}
return 1;
}
static int lupb_FieldDef_IsPacked(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushboolean(L, upb_FieldDef_IsPacked(f));
return 1;
}
static int lupb_FieldDef_MessageSubDef(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
const upb_MessageDef* m = upb_FieldDef_MessageSubDef(f);
lupb_wrapper_pushwrapper(L, 1, m, LUPB_MSGDEF);
return 1;
}
static int lupb_FieldDef_EnumSubDef(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
const upb_EnumDef* e = upb_FieldDef_EnumSubDef(f);
lupb_wrapper_pushwrapper(L, 1, e, LUPB_ENUMDEF);
return 1;
}
static int lupb_FieldDef_CType(lua_State* L) {
const upb_FieldDef* f = lupb_FieldDef_check(L, 1);
lua_pushinteger(L, upb_FieldDef_CType(f));
return 1;
}
static const struct luaL_Reg lupb_FieldDef_m[] = {
{"containing_oneof", lupb_FieldDef_ContainingOneof},
{"containing_type", lupb_FieldDef_ContainingType},
{"default", lupb_FieldDef_Default},
{"descriptor_type", lupb_FieldDef_Type},
{"has_subdef", lupb_FieldDef_HasSubDef},
{"index", lupb_FieldDef_Index},
{"is_extension", lupb_FieldDef_IsExtension},
{"label", lupb_FieldDef_Label},
{"name", lupb_FieldDef_Name},
{"number", lupb_FieldDef_Number},
{"packed", lupb_FieldDef_IsPacked},
{"msgsubdef", lupb_FieldDef_MessageSubDef},
{"enumsubdef", lupb_FieldDef_EnumSubDef},
{"type", lupb_FieldDef_CType},
{NULL, NULL}};
/* lupb_OneofDef **************************************************************/
const upb_OneofDef* lupb_OneofDef_check(lua_State* L, int narg) {
return lupb_wrapper_check(L, narg, LUPB_ONEOFDEF);
}
static int lupb_OneofDef_ContainingType(lua_State* L) {
const upb_OneofDef* o = lupb_OneofDef_check(L, 1);
const upb_MessageDef* m = upb_OneofDef_ContainingType(o);
lupb_wrapper_pushwrapper(L, 1, m, LUPB_MSGDEF);
return 1;
}
static int lupb_OneofDef_Field(lua_State* L) {
const upb_OneofDef* o = lupb_OneofDef_check(L, 1);
int32_t idx = lupb_checkint32(L, 2);
int count = upb_OneofDef_FieldCount(o);
if (idx < 0 || idx >= count) {
const char* msg =
lua_pushfstring(L, "index %d exceeds field count %d", idx, count);
return luaL_argerror(L, 2, msg);
}
lupb_wrapper_pushwrapper(L, 1, upb_OneofDef_Field(o, idx), LUPB_FIELDDEF);
return 1;
}
static int lupb_oneofiter_next(lua_State* L) {
const upb_OneofDef* o = lupb_OneofDef_check(L, lua_upvalueindex(1));
int* index = lua_touserdata(L, lua_upvalueindex(2));
const upb_FieldDef* f;
if (*index == upb_OneofDef_FieldCount(o)) return 0;
f = upb_OneofDef_Field(o, (*index)++);
lupb_wrapper_pushwrapper(L, lua_upvalueindex(1), f, LUPB_FIELDDEF);
return 1;
}
static int lupb_OneofDef_Fields(lua_State* L) {
int* index = lua_newuserdata(L, sizeof(int));
lupb_OneofDef_check(L, 1);
*index = 0;
/* Closure upvalues are: oneofdef, index. */
lua_pushcclosure(L, &lupb_oneofiter_next, 2);
return 1;
}
static int lupb_OneofDef_len(lua_State* L) {
const upb_OneofDef* o = lupb_OneofDef_check(L, 1);
lua_pushinteger(L, upb_OneofDef_FieldCount(o));
return 1;
}
/* lupb_OneofDef_lookupfield()
*
* Handles:
* oneof.lookup_field(field_number)
* oneof.lookup_field(field_name)
*/
static int lupb_OneofDef_lookupfield(lua_State* L) {
const upb_OneofDef* o = lupb_OneofDef_check(L, 1);
const upb_FieldDef* f;
switch (lua_type(L, 2)) {
case LUA_TNUMBER:
f = upb_OneofDef_LookupNumber(o, lua_tointeger(L, 2));
break;
case LUA_TSTRING:
f = upb_OneofDef_LookupName(o, lua_tostring(L, 2));
break;
default: {
const char* msg = lua_pushfstring(L, "number or string expected, got %s",
luaL_typename(L, 2));
return luaL_argerror(L, 2, msg);
}
}
lupb_wrapper_pushwrapper(L, 1, f, LUPB_FIELDDEF);
return 1;
}
static int lupb_OneofDef_Name(lua_State* L) {
const upb_OneofDef* o = lupb_OneofDef_check(L, 1);
lua_pushstring(L, upb_OneofDef_Name(o));
return 1;
}
static const struct luaL_Reg lupb_OneofDef_m[] = {
{"containing_type", lupb_OneofDef_ContainingType},
{"field", lupb_OneofDef_Field},
{"fields", lupb_OneofDef_Fields},
{"lookup_field", lupb_OneofDef_lookupfield},
{"name", lupb_OneofDef_Name},
{NULL, NULL}};
static const struct luaL_Reg lupb_OneofDef_mm[] = {{"__len", lupb_OneofDef_len},
{NULL, NULL}};
/* lupb_MessageDef
* ****************************************************************/
typedef struct {
const upb_MessageDef* md;
} lupb_MessageDef;
const upb_MessageDef* lupb_MessageDef_check(lua_State* L, int narg) {
return lupb_wrapper_check(L, narg, LUPB_MSGDEF);
}
static int lupb_MessageDef_FieldCount(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
lua_pushinteger(L, upb_MessageDef_FieldCount(m));
return 1;
}
static int lupb_MessageDef_OneofCount(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
lua_pushinteger(L, upb_MessageDef_OneofCount(m));
return 1;
}
static bool lupb_MessageDef_pushnested(lua_State* L, int msgdef, int name) {
const upb_MessageDef* m = lupb_MessageDef_check(L, msgdef);
lupb_wrapper_pushdefpool(L, msgdef);
upb_DefPool* defpool = lupb_DefPool_check(L, -1);
lua_pop(L, 1);
/* Construct full package.Message.SubMessage name. */
lua_pushstring(L, upb_MessageDef_FullName(m));
lua_pushstring(L, ".");
lua_pushvalue(L, name);
lua_concat(L, 3);
const char* nested_name = lua_tostring(L, -1);
/* Try lookup. */
const upb_MessageDef* nested =
upb_DefPool_FindMessageByName(defpool, nested_name);
if (!nested) return false;
lupb_wrapper_pushwrapper(L, msgdef, nested, LUPB_MSGDEF);
return true;
}
/* lupb_MessageDef_Field()
*
* Handles:
* msg.field(field_number) -> fielddef
* msg.field(field_name) -> fielddef
*/
static int lupb_MessageDef_Field(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
const upb_FieldDef* f;
switch (lua_type(L, 2)) {
case LUA_TNUMBER:
f = upb_MessageDef_FindFieldByNumber(m, lua_tointeger(L, 2));
break;
case LUA_TSTRING:
f = upb_MessageDef_FindFieldByName(m, lua_tostring(L, 2));
break;
default: {
const char* msg = lua_pushfstring(L, "number or string expected, got %s",
luaL_typename(L, 2));
return luaL_argerror(L, 2, msg);
}
}
lupb_wrapper_pushwrapper(L, 1, f, LUPB_FIELDDEF);
return 1;
}
/* lupb_MessageDef_FindByNameWithSize()
*
* Handles:
* msg.lookup_name(name) -> fielddef or oneofdef
*/
static int lupb_MessageDef_FindByNameWithSize(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
const upb_FieldDef* f;
const upb_OneofDef* o;
if (!upb_MessageDef_FindByName(m, lua_tostring(L, 2), &f, &o)) {
lua_pushnil(L);
} else if (o) {
lupb_wrapper_pushwrapper(L, 1, o, LUPB_ONEOFDEF);
} else {
lupb_wrapper_pushwrapper(L, 1, f, LUPB_FIELDDEF);
}
return 1;
}
/* lupb_MessageDef_Name()
*
* Handles:
* msg.name() -> string
*/
static int lupb_MessageDef_Name(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
lua_pushstring(L, upb_MessageDef_Name(m));
return 1;
}
static int lupb_msgfielditer_next(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, lua_upvalueindex(1));
int* index = lua_touserdata(L, lua_upvalueindex(2));
const upb_FieldDef* f;
if (*index == upb_MessageDef_FieldCount(m)) return 0;
f = upb_MessageDef_Field(m, (*index)++);
lupb_wrapper_pushwrapper(L, lua_upvalueindex(1), f, LUPB_FIELDDEF);
return 1;
}
static int lupb_MessageDef_Fields(lua_State* L) {
int* index = lua_newuserdata(L, sizeof(int));
lupb_MessageDef_check(L, 1);
*index = 0;
/* Closure upvalues are: msgdef, index. */
lua_pushcclosure(L, &lupb_msgfielditer_next, 2);
return 1;
}
static int lupb_MessageDef_File(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
const upb_FileDef* file = upb_MessageDef_File(m);
lupb_wrapper_pushwrapper(L, 1, file, LUPB_FILEDEF);
return 1;
}
static int lupb_MessageDef_FullName(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
lua_pushstring(L, upb_MessageDef_FullName(m));
return 1;
}
static int lupb_MessageDef_index(lua_State* L) {
if (!lupb_MessageDef_pushnested(L, 1, 2)) {
luaL_error(L, "No such nested message");
}
return 1;
}
static int lupb_msgoneofiter_next(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, lua_upvalueindex(1));
int* index = lua_touserdata(L, lua_upvalueindex(2));
const upb_OneofDef* o;
if (*index == upb_MessageDef_OneofCount(m)) return 0;
o = upb_MessageDef_Oneof(m, (*index)++);
lupb_wrapper_pushwrapper(L, lua_upvalueindex(1), o, LUPB_ONEOFDEF);
return 1;
}
static int lupb_MessageDef_Oneofs(lua_State* L) {
int* index = lua_newuserdata(L, sizeof(int));
lupb_MessageDef_check(L, 1);
*index = 0;
/* Closure upvalues are: msgdef, index. */
lua_pushcclosure(L, &lupb_msgoneofiter_next, 2);
return 1;
}
static int lupb_MessageDef_IsMapEntry(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
lua_pushboolean(L, upb_MessageDef_IsMapEntry(m));
return 1;
}
static int lupb_MessageDef_Syntax(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
lua_pushinteger(L, upb_MessageDef_Syntax(m));
return 1;
}
static int lupb_MessageDef_tostring(lua_State* L) {
const upb_MessageDef* m = lupb_MessageDef_check(L, 1);
lua_pushfstring(L, "<upb.MessageDef name=%s, field_count=%d>",
upb_MessageDef_FullName(m),
(int)upb_MessageDef_FieldCount(m));
return 1;
}
static const struct luaL_Reg lupb_MessageDef_mm[] = {
{"__call", lupb_MessageDef_call},
{"__index", lupb_MessageDef_index},
{"__len", lupb_MessageDef_FieldCount},
{"__tostring", lupb_MessageDef_tostring},
{NULL, NULL}};
static const struct luaL_Reg lupb_MessageDef_m[] = {
{"field", lupb_MessageDef_Field},
{"fields", lupb_MessageDef_Fields},
{"field_count", lupb_MessageDef_FieldCount},
{"file", lupb_MessageDef_File},
{"full_name", lupb_MessageDef_FullName},
{"lookup_name", lupb_MessageDef_FindByNameWithSize},
{"name", lupb_MessageDef_Name},
{"oneof_count", lupb_MessageDef_OneofCount},
{"oneofs", lupb_MessageDef_Oneofs},
{"syntax", lupb_MessageDef_Syntax},
{"_map_entry", lupb_MessageDef_IsMapEntry},
{NULL, NULL}};
/* lupb_EnumDef ***************************************************************/
const upb_EnumDef* lupb_EnumDef_check(lua_State* L, int narg) {
return lupb_wrapper_check(L, narg, LUPB_ENUMDEF);
}
static int lupb_EnumDef_len(lua_State* L) {
const upb_EnumDef* e = lupb_EnumDef_check(L, 1);
lua_pushinteger(L, upb_EnumDef_ValueCount(e));
return 1;
}
static int lupb_EnumDef_File(lua_State* L) {
const upb_EnumDef* e = lupb_EnumDef_check(L, 1);
const upb_FileDef* file = upb_EnumDef_File(e);
lupb_wrapper_pushwrapper(L, 1, file, LUPB_FILEDEF);
return 1;
}
/* lupb_EnumDef_Value()
*
* Handles:
* enum.value(number) -> enumval
* enum.value(name) -> enumval
*/
static int lupb_EnumDef_Value(lua_State* L) {
const upb_EnumDef* e = lupb_EnumDef_check(L, 1);
const upb_EnumValueDef* ev;
switch (lua_type(L, 2)) {
case LUA_TNUMBER:
ev = upb_EnumDef_FindValueByNumber(e, lupb_checkint32(L, 2));
break;
case LUA_TSTRING:
ev = upb_EnumDef_FindValueByName(e, lua_tostring(L, 2));
break;
default: {
const char* msg = lua_pushfstring(L, "number or string expected, got %s",
luaL_typename(L, 2));
return luaL_argerror(L, 2, msg);
}
}
lupb_wrapper_pushwrapper(L, 1, ev, LUPB_ENUMVALDEF);
return 1;
}
static const struct luaL_Reg lupb_EnumDef_mm[] = {{"__len", lupb_EnumDef_len},
{NULL, NULL}};
static const struct luaL_Reg lupb_EnumDef_m[] = {
{"file", lupb_EnumDef_File}, {"value", lupb_EnumDef_Value}, {NULL, NULL}};
/* lupb_EnumValueDef
* ************************************************************/
const upb_EnumValueDef* lupb_enumvaldef_check(lua_State* L, int narg) {
return lupb_wrapper_check(L, narg, LUPB_ENUMVALDEF);
}
static int lupb_EnumValueDef_Enum(lua_State* L) {
const upb_EnumValueDef* ev = lupb_enumvaldef_check(L, 1);
const upb_EnumDef* e = upb_EnumValueDef_Enum(ev);
lupb_wrapper_pushwrapper(L, 1, e, LUPB_ENUMDEF);
return 1;
}
static int lupb_EnumValueDef_FullName(lua_State* L) {
const upb_EnumValueDef* ev = lupb_enumvaldef_check(L, 1);
lua_pushstring(L, upb_EnumValueDef_FullName(ev));
return 1;
}
static int lupb_EnumValueDef_Name(lua_State* L) {
const upb_EnumValueDef* ev = lupb_enumvaldef_check(L, 1);
lua_pushstring(L, upb_EnumValueDef_Name(ev));
return 1;
}
static int lupb_EnumValueDef_Number(lua_State* L) {
const upb_EnumValueDef* ev = lupb_enumvaldef_check(L, 1);
lupb_pushint32(L, upb_EnumValueDef_Number(ev));
return 1;
}
static const struct luaL_Reg lupb_enumvaldef_m[] = {
{"enum", lupb_EnumValueDef_Enum},
{"full_name", lupb_EnumValueDef_FullName},
{"name", lupb_EnumValueDef_Name},
{"number", lupb_EnumValueDef_Number},
{NULL, NULL}};
/* lupb_FileDef ***************************************************************/
const upb_FileDef* lupb_FileDef_check(lua_State* L, int narg) {
return lupb_wrapper_check(L, narg, LUPB_FILEDEF);
}
static int lupb_FileDef_Dependency(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
int index = luaL_checkint(L, 2);
const upb_FileDef* dep = upb_FileDef_Dependency(f, index);
lupb_wrapper_pushwrapper(L, 1, dep, LUPB_FILEDEF);
return 1;
}
static int lupb_FileDef_DependencyCount(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
lua_pushnumber(L, upb_FileDef_DependencyCount(f));
return 1;
}
static int lupb_FileDef_enum(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
int index = luaL_checkint(L, 2);
const upb_EnumDef* e = upb_FileDef_TopLevelEnum(f, index);
lupb_wrapper_pushwrapper(L, 1, e, LUPB_ENUMDEF);
return 1;
}
static int lupb_FileDef_enumcount(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
lua_pushnumber(L, upb_FileDef_TopLevelEnumCount(f));
return 1;
}
static int lupb_FileDef_msg(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
int index = luaL_checkint(L, 2);
const upb_MessageDef* m = upb_FileDef_TopLevelMessage(f, index);
lupb_wrapper_pushwrapper(L, 1, m, LUPB_MSGDEF);
return 1;
}
static int lupb_FileDef_msgcount(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
lua_pushnumber(L, upb_FileDef_TopLevelMessageCount(f));
return 1;
}
static int lupb_FileDef_Name(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
lua_pushstring(L, upb_FileDef_Name(f));
return 1;
}
static int lupb_FileDef_Package(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
lua_pushstring(L, upb_FileDef_Package(f));
return 1;
}
static int lupb_FileDef_Pool(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
const upb_DefPool* defpool = upb_FileDef_Pool(f);
lupb_wrapper_pushwrapper(L, 1, defpool, LUPB_SYMTAB);
return 1;
}
static int lupb_FileDef_Syntax(lua_State* L) {
const upb_FileDef* f = lupb_FileDef_check(L, 1);
lua_pushnumber(L, upb_FileDef_Syntax(f));
return 1;
}
static const struct luaL_Reg lupb_FileDef_m[] = {
{"dep", lupb_FileDef_Dependency},
{"depcount", lupb_FileDef_DependencyCount},
{"enum", lupb_FileDef_enum},
{"enumcount", lupb_FileDef_enumcount},
{"msg", lupb_FileDef_msg},
{"msgcount", lupb_FileDef_msgcount},
{"name", lupb_FileDef_Name},
{"package", lupb_FileDef_Package},
{"defpool", lupb_FileDef_Pool},
{"syntax", lupb_FileDef_Syntax},
{NULL, NULL}};
/* lupb_DefPool
* ****************************************************************/
/* The defpool owns all defs. Thus GC-rooting the defpool ensures that all
* underlying defs stay alive.
*
* The defpool's userval is a cache of def* -> object. */
#define LUPB_CACHE_INDEX 1
typedef struct {
upb_DefPool* defpool;
} lupb_DefPool;
upb_DefPool* lupb_DefPool_check(lua_State* L, int narg) {
lupb_DefPool* ldefpool = luaL_checkudata(L, narg, LUPB_SYMTAB);
if (!ldefpool->defpool) {
luaL_error(L, "called into dead object");
}
return ldefpool->defpool;
}
void lupb_DefPool_pushwrapper(lua_State* L, int narg, const void* def,
const char* type) {
narg = lua_absindex(L, narg);
assert(luaL_testudata(L, narg, LUPB_SYMTAB));
if (def == NULL) {
lua_pushnil(L);
return;
}
lua_getiuservalue(L, narg, LUPB_CACHE_INDEX); /* Get cache. */
/* Index by "def" pointer. */
lua_rawgetp(L, -1, def);
/* Stack is now: cache, cached value. */
if (lua_isnil(L, -1)) {
/* Create new wrapper. */
lupb_wrapper* w = lupb_newuserdata(L, sizeof(*w), 1, type);
w->def = def;
lua_replace(L, -2); /* Replace nil */
/* Set defpool as userval. */
lua_pushvalue(L, narg);
lua_setiuservalue(L, -2, LUPB_SYMTAB_INDEX);
/* Add wrapper to the the cache. */
lua_pushvalue(L, -1);
lua_rawsetp(L, -3, def);
}
lua_replace(L, -2); /* Remove cache, leaving only the wrapper. */
}
/* upb_DefPool_New()
*
* Handles:
* upb.DefPool() -> <new instance>
*/
static int lupb_DefPool_New(lua_State* L) {
lupb_DefPool* ldefpool =
lupb_newuserdata(L, sizeof(*ldefpool), 1, LUPB_SYMTAB);
ldefpool->defpool = upb_DefPool_New();
/* Create our object cache. */
lua_newtable(L);
/* Cache metatable: specifies that values are weak. */
lua_createtable(L, 0, 1);
lua_pushstring(L, "v");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
/* Put the defpool itself in the cache metatable. */
lua_pushvalue(L, -2);
lua_rawsetp(L, -2, ldefpool->defpool);
/* Set the cache as our userval. */
lua_setiuservalue(L, -2, LUPB_CACHE_INDEX);
return 1;
}
static int lupb_DefPool_gc(lua_State* L) {
lupb_DefPool* ldefpool = luaL_checkudata(L, 1, LUPB_SYMTAB);
upb_DefPool_Free(ldefpool->defpool);
ldefpool->defpool = NULL;
return 0;
}
static int lupb_DefPool_AddFile(lua_State* L) {
size_t len;
upb_DefPool* s = lupb_DefPool_check(L, 1);
const char* str = luaL_checklstring(L, 2, &len);
upb_Arena* arena = lupb_Arena_pushnew(L);
const google_protobuf_FileDescriptorProto* file;
const upb_FileDef* file_def;
upb_Status status;
upb_Status_Clear(&status);
file = google_protobuf_FileDescriptorProto_parse(str, len, arena);
if (!file) {
luaL_argerror(L, 2, "failed to parse descriptor");
}
file_def = upb_DefPool_AddFile(s, file, &status);
lupb_checkstatus(L, &status);
lupb_DefPool_pushwrapper(L, 1, file_def, LUPB_FILEDEF);
return 1;
}
static int lupb_DefPool_addset(lua_State* L) {
size_t i, n, len;
const google_protobuf_FileDescriptorProto* const* files;
google_protobuf_FileDescriptorSet* set;
upb_DefPool* s = lupb_DefPool_check(L, 1);
const char* str = luaL_checklstring(L, 2, &len);
upb_Arena* arena = lupb_Arena_pushnew(L);
upb_Status status;
upb_Status_Clear(&status);
set = google_protobuf_FileDescriptorSet_parse(str, len, arena);
if (!set) {
luaL_argerror(L, 2, "failed to parse descriptor");
}
files = google_protobuf_FileDescriptorSet_file(set, &n);
for (i = 0; i < n; i++) {
upb_DefPool_AddFile(s, files[i], &status);
lupb_checkstatus(L, &status);
}
return 0;
}
static int lupb_DefPool_FindMessageByName(lua_State* L) {
const upb_DefPool* s = lupb_DefPool_check(L, 1);
const upb_MessageDef* m =
upb_DefPool_FindMessageByName(s, luaL_checkstring(L, 2));
lupb_DefPool_pushwrapper(L, 1, m, LUPB_MSGDEF);
return 1;
}
static int lupb_DefPool_FindEnumByName(lua_State* L) {
const upb_DefPool* s = lupb_DefPool_check(L, 1);
const upb_EnumDef* e = upb_DefPool_FindEnumByName(s, luaL_checkstring(L, 2));
lupb_DefPool_pushwrapper(L, 1, e, LUPB_ENUMDEF);
return 1;
}
static int lupb_DefPool_FindEnumByNameval(lua_State* L) {
const upb_DefPool* s = lupb_DefPool_check(L, 1);
const upb_EnumValueDef* e =
upb_DefPool_FindEnumByNameval(s, luaL_checkstring(L, 2));
lupb_DefPool_pushwrapper(L, 1, e, LUPB_ENUMVALDEF);
return 1;
}
static int lupb_DefPool_tostring(lua_State* L) {
lua_pushfstring(L, "<upb.DefPool>");
return 1;
}
static const struct luaL_Reg lupb_DefPool_m[] = {
{"add_file", lupb_DefPool_AddFile},
{"add_set", lupb_DefPool_addset},
{"lookup_msg", lupb_DefPool_FindMessageByName},
{"lookup_enum", lupb_DefPool_FindEnumByName},
{"lookup_enumval", lupb_DefPool_FindEnumByNameval},
{NULL, NULL}};
static const struct luaL_Reg lupb_DefPool_mm[] = {
{"__gc", lupb_DefPool_gc},
{"__tostring", lupb_DefPool_tostring},
{NULL, NULL}};
/* lupb toplevel **************************************************************/
static void lupb_setfieldi(lua_State* L, const char* field, int i) {
lua_pushinteger(L, i);
lua_setfield(L, -2, field);
}
static const struct luaL_Reg lupbdef_toplevel_m[] = {
{"DefPool", lupb_DefPool_New}, {NULL, NULL}};
void lupb_def_registertypes(lua_State* L) {
lupb_setfuncs(L, lupbdef_toplevel_m);
/* Register types. */
lupb_register_type(L, LUPB_ENUMDEF, lupb_EnumDef_m, lupb_EnumDef_mm);
lupb_register_type(L, LUPB_ENUMVALDEF, lupb_enumvaldef_m, NULL);
lupb_register_type(L, LUPB_FIELDDEF, lupb_FieldDef_m, NULL);
lupb_register_type(L, LUPB_FILEDEF, lupb_FileDef_m, NULL);
lupb_register_type(L, LUPB_MSGDEF, lupb_MessageDef_m, lupb_MessageDef_mm);
lupb_register_type(L, LUPB_ONEOFDEF, lupb_OneofDef_m, lupb_OneofDef_mm);
lupb_register_type(L, LUPB_SYMTAB, lupb_DefPool_m, lupb_DefPool_mm);
/* Register constants. */
lupb_setfieldi(L, "LABEL_OPTIONAL", kUpb_Label_Optional);
lupb_setfieldi(L, "LABEL_REQUIRED", kUpb_Label_Required);
lupb_setfieldi(L, "LABEL_REPEATED", kUpb_Label_Repeated);
lupb_setfieldi(L, "TYPE_DOUBLE", kUpb_CType_Double);
lupb_setfieldi(L, "TYPE_FLOAT", kUpb_CType_Float);
lupb_setfieldi(L, "TYPE_INT64", kUpb_CType_Int64);
lupb_setfieldi(L, "TYPE_UINT64", kUpb_CType_UInt64);
lupb_setfieldi(L, "TYPE_INT32", kUpb_CType_Int32);
lupb_setfieldi(L, "TYPE_BOOL", kUpb_CType_Bool);
lupb_setfieldi(L, "TYPE_STRING", kUpb_CType_String);
lupb_setfieldi(L, "TYPE_MESSAGE", kUpb_CType_Message);
lupb_setfieldi(L, "TYPE_BYTES", kUpb_CType_Bytes);
lupb_setfieldi(L, "TYPE_UINT32", kUpb_CType_UInt32);
lupb_setfieldi(L, "TYPE_ENUM", kUpb_CType_Enum);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_DOUBLE", kUpb_FieldType_Double);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_FLOAT", kUpb_FieldType_Float);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT64", kUpb_FieldType_Int64);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT64", kUpb_FieldType_UInt64);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT32", kUpb_FieldType_Int32);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED64", kUpb_FieldType_Fixed64);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED32", kUpb_FieldType_Fixed32);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_BOOL", kUpb_FieldType_Bool);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_STRING", kUpb_FieldType_String);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_GROUP", kUpb_FieldType_Group);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_MESSAGE", kUpb_FieldType_Message);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_BYTES", kUpb_FieldType_Bytes);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT32", kUpb_FieldType_UInt32);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_ENUM", kUpb_FieldType_Enum);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED32", kUpb_FieldType_SFixed32);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED64", kUpb_FieldType_SFixed64);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT32", kUpb_FieldType_SInt32);
lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT64", kUpb_FieldType_SInt64);
lupb_setfieldi(L, "SYNTAX_PROTO2", kUpb_Syntax_Proto2);
lupb_setfieldi(L, "SYNTAX_PROTO3", kUpb_Syntax_Proto3);
}

@ -0,0 +1,154 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""lua_proto_library(): a rule for building Lua protos."""
load("@bazel_skylib//lib:paths.bzl", "paths")
# Generic support code #########################################################
# begin:github_only
_is_google3 = False
# end:github_only
# begin:google_only
# _is_google3 = True
# end:google_only
def _get_real_short_path(file):
# For some reason, files from other archives have short paths that look like:
# ../com_google_protobuf/google/protobuf/descriptor.proto
short_path = file.short_path
if short_path.startswith("../"):
second_slash = short_path.index("/", 3)
short_path = short_path[second_slash + 1:]
# Sometimes it has another few prefixes like:
# _virtual_imports/any_proto/google/protobuf/any.proto
# benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto
# We want just google/protobuf/any.proto.
virtual_imports = "_virtual_imports/"
if virtual_imports in short_path:
short_path = short_path.split(virtual_imports)[1].split("/", 1)[1]
return short_path
def _get_real_root(ctx, file):
real_short_path = _get_real_short_path(file)
root = file.path[:-len(real_short_path) - 1]
if not _is_google3 and ctx.rule.attr.strip_import_prefix:
root = paths.join(root, ctx.rule.attr.strip_import_prefix[1:])
return root
def _generate_output_file(ctx, src, extension):
package = ctx.label.package
if not _is_google3 and ctx.rule.attr.strip_import_prefix and ctx.rule.attr.strip_import_prefix != "/":
package = package[len(ctx.rule.attr.strip_import_prefix):]
real_short_path = _get_real_short_path(src)
real_short_path = paths.relativize(real_short_path, package)
output_filename = paths.replace_extension(real_short_path, extension)
ret = ctx.actions.declare_file(output_filename)
return ret
# upb_proto_library / upb_proto_reflection_library shared code #################
_LuaFilesInfo = provider(
"A set of lua files generated from .proto files",
fields = ["files"],
)
def _compile_upb_protos(ctx, proto_info, proto_sources):
files = [_generate_output_file(ctx, name, "_pb.lua") for name in proto_sources]
transitive_sets = proto_info.transitive_descriptor_sets.to_list()
ctx.actions.run(
inputs = depset(
direct = [proto_info.direct_descriptor_set],
transitive = [proto_info.transitive_descriptor_sets],
),
tools = [ctx.executable._upbc],
outputs = files,
executable = ctx.executable._protoc,
arguments = [
"--lua_out=" + _get_real_root(ctx, files[0]),
"--plugin=protoc-gen-lua=" + ctx.executable._upbc.path,
"--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]),
] +
[_get_real_short_path(file) for file in proto_sources],
progress_message = "Generating Lua protos for :" + ctx.label.name,
)
return files
def _lua_proto_rule_impl(ctx):
if len(ctx.attr.deps) != 1:
fail("only one deps dependency allowed.")
dep = ctx.attr.deps[0]
if _LuaFilesInfo not in dep:
fail("proto_library rule must generate _LuaFilesInfo (aspect should have handled this).")
files = dep[_LuaFilesInfo].files
return [
DefaultInfo(
files = files,
data_runfiles = ctx.runfiles(files = files.to_list()),
),
]
def _lua_proto_library_aspect_impl(target, ctx):
proto_info = target[ProtoInfo]
files = _compile_upb_protos(ctx, proto_info, proto_info.direct_sources)
deps = ctx.rule.attr.deps
transitive = [dep[_LuaFilesInfo].files for dep in deps if _LuaFilesInfo in dep]
return [_LuaFilesInfo(files = depset(direct = files, transitive = transitive))]
# lua_proto_library() ##########################################################
_lua_proto_library_aspect = aspect(
attrs = {
"_upbc": attr.label(
executable = True,
cfg = "exec",
default = "//lua:protoc-gen-lua",
),
"_protoc": attr.label(
executable = True,
cfg = "exec",
default = "@com_google_protobuf//:protoc",
),
},
implementation = _lua_proto_library_aspect_impl,
provides = [_LuaFilesInfo],
attr_aspects = ["deps"],
fragments = ["cpp"],
)
lua_proto_library = rule(
output_to_genfiles = True,
implementation = _lua_proto_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [_lua_proto_library_aspect],
allow_rules = ["proto_library"],
providers = [ProtoInfo],
),
},
)

@ -0,0 +1,96 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <signal.h>
#include "lua/upb.h"
lua_State* L;
static void interrupt(lua_State* L, lua_Debug* ar) {
(void)ar;
lua_sethook(L, NULL, 0, 0);
luaL_error(L, "SIGINT");
}
static void sighandler(int i) {
fprintf(stderr, "Signal!\n");
signal(i, SIG_DFL);
lua_sethook(L, interrupt, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}
const char* init =
"package.preload['lupb'] = ... "
"package.path = '"
"./?.lua;"
"./third_party/lunit/?.lua;"
"external/com_google_protobuf/?.lua;"
"external/com_google_protobuf/src/?.lua;"
"bazel-bin/?.lua;"
"bazel-bin/external/com_google_protobuf/src/?.lua;"
"bazel-bin/external/com_google_protobuf/?.lua;"
"lua/?.lua;"
// These additional paths handle the case where this test is invoked from
// the protobuf repo's Bazel workspace.
"external/upb/?.lua;"
"external/upb/third_party/lunit/?.lua;"
"src/?.lua;"
"bazel-bin/external/upb/?.lua;"
"external/upb/lua/?.lua"
"'";
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "missing argument with path to .lua file\n");
return 1;
}
int ret = 0;
L = luaL_newstate();
luaL_openlibs(L);
lua_pushcfunction(L, luaopen_lupb);
ret = luaL_loadstring(L, init);
lua_pushcfunction(L, luaopen_lupb);
signal(SIGINT, sighandler);
ret = ret || lua_pcall(L, 1, LUA_MULTRET, 0) || luaL_dofile(L, argv[1]);
signal(SIGINT, SIG_DFL);
if (ret) {
fprintf(stderr, "error testing Lua: %s\n", lua_tostring(L, -1));
ret = 1;
}
lua_close(L);
return ret;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,98 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
import "google/protobuf/timestamp.proto";
package upb_lua_test;
message MapTest {
map<string, double> map_string_double = 1;
}
message PackedTest {
repeated bool bool_packed = 1 [packed = true];
repeated int32 i32_packed = 2 [packed = true];
repeated int64 i64_packed = 3 [packed = true];
repeated fixed32 f32_packed = 4 [packed = true];
repeated fixed64 f64_packed = 5 [packed = true];
}
message UnpackedTest {
repeated bool bool_packed = 1 [packed = false];
repeated int32 i32_packed = 2 [packed = false];
repeated int64 i64_packed = 3 [packed = false];
repeated fixed32 f32_packed = 4 [packed = false];
repeated fixed64 f64_packed = 5 [packed = false];
}
message TestLargeFieldNumber {
optional int32 i32 = 456214797;
}
message TestTimestamp {
optional google.protobuf.Timestamp ts = 1;
}
message HelloRequest {
optional uint32 id = 1;
optional uint32 random_name_a0 = 2;
optional uint32 random_name_a1 = 3;
optional uint32 random_name_a2 = 4;
optional uint32 random_name_a3 = 5;
optional uint32 random_name_a4 = 6;
optional uint32 random_name_a5 = 7;
optional uint32 random_name_a6 = 8;
optional uint32 random_name_a7 = 9;
optional uint32 random_name_a8 = 10;
optional uint32 random_name_a9 = 11;
optional uint32 random_name_b0 = 12;
optional uint32 random_name_b1 = 13;
optional uint32 random_name_b2 = 14;
optional uint32 random_name_b3 = 15;
optional uint32 random_name_b4 = 16;
optional uint32 random_name_b5 = 17;
optional uint32 random_name_b6 = 18;
optional uint32 random_name_b7 = 19;
optional uint32 random_name_b8 = 20;
optional uint32 random_name_b9 = 21;
optional uint32 random_name_c0 = 22;
optional uint32 random_name_c1 = 23;
optional uint32 random_name_c2 = 24;
optional uint32 random_name_c3 = 25;
optional uint32 random_name_c4 = 26;
optional uint32 random_name_c5 = 27;
optional uint32 random_name_c6 = 28;
optional uint32 random_name_c7 = 29;
optional uint32 random_name_c8 = 30;
optional uint32 random_name_c9 = 31;
optional string version = 32;
}

@ -0,0 +1,852 @@
--[[--------------------------------------------------------------------------
Protocol Buffers - Google's data interchange format
Copyright 2023 Google LLC. All rights reserved.
https://developers.google.com/protocol-buffers/
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]--------------------------------------------------------------------------
local upb = require "lupb"
local lunit = require "lunit"
local upb_test = require "lua.test_pb"
local test_messages_proto3 = require "google.protobuf.test_messages_proto3_pb"
local test_messages_proto2 = require "google.protobuf.test_messages_proto2_pb"
local descriptor = require "google.protobuf.descriptor_pb"
local empty = require "google.protobuf.empty_pb"
if _VERSION >= 'Lua 5.2' then
_ENV = lunit.module("testupb", "seeall")
else
module("testupb", lunit.testcase, package.seeall)
end
function iter_to_array(iter)
local arr = {}
for v in iter do
arr[#arr + 1] = v
end
return arr
end
function test_def_readers()
local m = test_messages_proto3.TestAllTypesProto3
assert_equal("TestAllTypesProto3", m:name())
assert_equal("protobuf_test_messages.proto3.TestAllTypesProto3", m:full_name())
-- field
local f = m:field("optional_int32")
local f2 = m:field(1)
assert_equal(f, f2)
assert_equal(1, f:number())
assert_equal("optional_int32", f:name())
assert_equal(upb.LABEL_OPTIONAL, f:label())
assert_equal(upb.DESCRIPTOR_TYPE_INT32, f:descriptor_type())
assert_equal(upb.TYPE_INT32, f:type())
assert_nil(f:containing_oneof())
assert_equal(m, f:containing_type())
assert_equal(0, f:default())
local message_field_count = 0
for field in m:fields() do
message_field_count = message_field_count + 1
end
assert_equal(message_field_count, #m)
local message_oneof_count = 0
for oneof in m:oneofs() do
message_oneof_count = message_oneof_count + 1
end
assert_equal(message_oneof_count, m:oneof_count())
-- oneof
local o = m:lookup_name("oneof_field")
assert_equal("oneof_field", o:name())
assert_equal(m, o:containing_type())
local oneof_field_count = 0
for field in o:fields() do
oneof_field_count = oneof_field_count + 1
end
assert_equal(oneof_field_count, #o)
-- enum
local e = test_messages_proto3['TestAllTypesProto3.NestedEnum']
assert_true(#e > 3 and #e < 10)
assert_equal(2, e:value("BAZ"):number())
end
function test_msg_map()
msg = test_messages_proto3.TestAllTypesProto3()
msg.map_int32_int32[5] = 10
msg.map_int32_int32[6] = 12
assert_equal(10, msg.map_int32_int32[5])
assert_equal(12, msg.map_int32_int32[6])
-- Test overwrite.
msg.map_int32_int32[5] = 20
assert_equal(20, msg.map_int32_int32[5])
assert_equal(12, msg.map_int32_int32[6])
msg.map_int32_int32[5] = 10
-- Test delete.
msg.map_int32_int32[5] = nil
assert_nil(msg.map_int32_int32[5])
assert_equal(12, msg.map_int32_int32[6])
msg.map_int32_int32[5] = 10
local serialized = upb.encode(msg)
assert_true(#serialized > 0)
local msg2 = upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
assert_equal(10, msg2.map_int32_int32[5])
assert_equal(12, msg2.map_int32_int32[6])
end
function test_map_sorting()
function msg_with_int32_entries(start, expand)
local msg = test_messages_proto3.TestAllTypesProto3()
for i=start,start + 8 do
msg.map_int32_int32[i] = i * 2
end
if expand then
for i=start+20,200 do
msg.map_int32_int32[i] = i
end
for i=start+20,200 do
msg.map_int32_int32[i] = nil
end
end
return msg
end
function msg_with_msg_entries(expand)
local msg = test_messages_proto3.TestAllTypesProto3()
-- 8! = 40320 possible orderings makes it overwhelmingly likely that two
-- random orderings will be different.
for i=1,8 do
local submsg = test_messages_proto3.TestAllTypesProto3.NestedMessage()
submsg.corecursive = msg_with_int32_entries(i, expand)
msg.map_string_nested_message[tostring(i)] = submsg
end
expand = false
if expand then
for i=21,2000 do
local submsg = test_messages_proto3.TestAllTypesProto3.NestedMessage()
submsg.corecursive = msg_with_int32_entries(i, expand)
msg.map_string_nested_message[tostring(i)] = submsg
end
for i=21,2000 do
msg.map_string_nested_message[tostring(i)] = nil
end
end
return msg
end
-- Create two messages with the same contents but (hopefully) different
-- map table orderings.
local msg = msg_with_msg_entries(false)
local msg2 = msg_with_msg_entries(true)
local text1 = upb.text_encode(msg)
local text2 = upb.text_encode(msg2)
assert_equal(text1, text2)
local binary1 = upb.encode(msg, {upb.ENCODE_DETERMINISTIC})
local binary2 = upb.encode(msg2, {upb.ENCODE_DETERMINISTIC})
assert_equal(binary1, binary2)
-- Non-sorted map should compare different.
local text3 = upb.text_encode(msg, {upb.TXTENC_NOSORT})
assert_not_equal(text1, text3)
local binary3 = upb.encode(msg)
assert_not_equal(binary1, binary3)
end
function test_utf8()
local proto2_msg = test_messages_proto2.TestAllTypesProto2()
proto2_msg.optional_string = "\xff"
local serialized = upb.encode(proto2_msg)
-- Decoding invalid UTF-8 succeeds in proto2.
upb.decode(test_messages_proto2.TestAllTypesProto2, serialized)
-- Decoding invalid UTF-8 fails in proto2.
assert_error_match("Error decoding protobuf", function()
upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
end)
-- TODO(haberman): should proto3 accessors also check UTF-8 at set time?
end
function test_string_double_map()
msg = upb_test.MapTest()
msg.map_string_double["one"] = 1.0
msg.map_string_double["two point five"] = 2.5
assert_equal(1, msg.map_string_double["one"])
assert_equal(2.5, msg.map_string_double["two point five"])
-- Test overwrite.
msg.map_string_double["one"] = 2
assert_equal(2, msg.map_string_double["one"])
assert_equal(2.5, msg.map_string_double["two point five"])
msg.map_string_double["one"] = 1.0
-- Test delete.
msg.map_string_double["one"] = nil
assert_nil(msg.map_string_double["one"])
assert_equal(2.5, msg.map_string_double["two point five"])
msg.map_string_double["one"] = 1
local serialized = upb.encode(msg)
assert_true(#serialized > 0)
local msg2 = upb.decode(upb_test.MapTest, serialized)
assert_equal(1, msg2.map_string_double["one"])
assert_equal(2.5, msg2.map_string_double["two point five"])
end
function test_string_double_map()
local function fill_msg(msg)
msg.i32_packed[1] = 100
msg.i32_packed[2] = 200
msg.i32_packed[3] = 50000
msg.i64_packed[1] = 101
msg.i64_packed[2] = 201
msg.i64_packed[3] = 50001
msg.f32_packed[1] = 102
msg.f32_packed[2] = 202
msg.f32_packed[3] = 50002
msg.f64_packed[1] = 103
msg.f64_packed[2] = 203
msg.f64_packed[3] = 50003
end
local function check_msg(msg)
assert_equal(100, msg.i32_packed[1])
assert_equal(200, msg.i32_packed[2])
assert_equal(50000, msg.i32_packed[3])
assert_equal(3, #msg.i32_packed)
assert_equal(101, msg.i64_packed[1])
assert_equal(201, msg.i64_packed[2])
assert_equal(50001, msg.i64_packed[3])
assert_equal(3, #msg.i64_packed)
assert_equal(102, msg.f32_packed[1])
assert_equal(202, msg.f32_packed[2])
assert_equal(50002, msg.f32_packed[3])
assert_equal(3, #msg.f32_packed)
assert_equal(103, msg.f64_packed[1])
assert_equal(203, msg.f64_packed[2])
assert_equal(50003, msg.f64_packed[3])
assert_equal(3, #msg.f64_packed)
end
local msg = upb_test.PackedTest()
fill_msg(msg)
check_msg(msg)
local serialized_packed = upb.encode(msg)
local msg2 = upb.decode(upb_test.PackedTest, serialized_packed)
local msg3 = upb.decode(upb_test.UnpackedTest, serialized_packed)
check_msg(msg2)
check_msg(msg3)
serialized_unpacked = upb.encode(msg3)
local msg4 = upb.decode(upb_test.PackedTest, serialized_unpacked)
local msg5 = upb.decode(upb_test.PackedTest, serialized_unpacked)
check_msg(msg4)
check_msg(msg5)
end
function test_msg_string_map()
msg = test_messages_proto3.TestAllTypesProto3()
msg.map_string_string["foo"] = "bar"
msg.map_string_string["baz"] = "quux"
assert_nil(msg.map_string_string["abc"])
assert_equal("bar", msg.map_string_string["foo"])
assert_equal("quux", msg.map_string_string["baz"])
-- Test overwrite.
msg.map_string_string["foo"] = "123"
assert_equal("123", msg.map_string_string["foo"])
assert_equal("quux", msg.map_string_string["baz"])
msg.map_string_string["foo"] = "bar"
-- Test delete
msg.map_string_string["foo"] = nil
assert_nil(msg.map_string_string["foo"])
assert_equal("quux", msg.map_string_string["baz"])
msg.map_string_string["foo"] = "bar"
local serialized = upb.encode(msg)
assert_true(#serialized > 0)
local msg2 = upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
assert_equal("bar", msg2.map_string_string["foo"])
assert_equal("quux", msg2.map_string_string["baz"])
end
function test_msg_array()
msg = test_messages_proto3.TestAllTypesProto3()
assert_not_nil(msg.repeated_int32)
assert_equal(msg.repeated_int32, msg.repeated_int32)
assert_equal(0, #msg.repeated_int32)
msg.repeated_int32[1] = 2
assert_equal(1, #msg.repeated_int32);
assert_equal(2, msg.repeated_int32[1]);
-- Can't assign a scalar; array is expected.
assert_error_match("lupb.array expected", function() msg.repeated_int32 = 5 end)
-- Can't assign array of the wrong type.
local function assign_int64()
msg.repeated_int32 = upb.Array(upb.TYPE_INT64)
end
assert_error_match("array type mismatch", assign_int64)
local arr = upb.Array(upb.TYPE_INT32)
arr[1] = 6
assert_equal(1, #arr)
msg.repeated_int32 = arr
assert_equal(msg.repeated_int32, msg.repeated_int32)
assert_equal(arr, msg.repeated_int32)
assert_equal(1, #msg.repeated_int32)
assert_equal(6, msg.repeated_int32[1])
-- Can't assign other Lua types.
assert_error_match("array expected", function() msg.repeated_int32 = "abc" end)
assert_error_match("array expected", function() msg.repeated_int32 = true end)
assert_error_match("array expected", function() msg.repeated_int32 = false end)
assert_error_match("array expected", function() msg.repeated_int32 = nil end)
assert_error_match("array expected", function() msg.repeated_int32 = {} end)
assert_error_match("array expected", function() msg.repeated_int32 = print end)
end
function test_array_append()
local arr = upb.Array(upb.TYPE_INT32)
for i=1,200000 do
arr[i] = i
end
for i=1,200000 do
assert_equal(i, arr[i])
end
end
function test_msg_submsg()
--msg = test_messages_proto3.TestAllTypesProto3()
msg = test_messages_proto3['TestAllTypesProto3']()
assert_nil(msg.optional_nested_message)
-- Can't assign message of the wrong type.
local function assign_int64()
msg.optional_nested_message = test_messages_proto3.TestAllTypesProto3()
end
assert_error_match("message type mismatch", assign_int64)
local nested = test_messages_proto3['TestAllTypesProto3.NestedMessage']()
msg.optional_nested_message = nested
assert_equal(nested, msg.optional_nested_message)
-- Can't assign other Lua types.
assert_error_match("msg expected", function() msg.optional_nested_message = "abc" end)
assert_error_match("msg expected", function() msg.optional_nested_message = true end)
assert_error_match("msg expected", function() msg.optional_nested_message = false end)
assert_error_match("msg expected", function() msg.optional_nested_message = nil end)
assert_error_match("msg expected", function() msg.optional_nested_message = {} end)
assert_error_match("msg expected", function() msg.optional_nested_message = print end)
end
-- Lua 5.1 and 5.2 have slightly different semantics for how a finalizer
-- can be defined in Lua.
if _VERSION >= 'Lua 5.2' then
function defer(fn)
setmetatable({}, { __gc = fn })
end
else
function defer(fn)
getmetatable(newproxy(true)).__gc = fn
end
end
function test_finalizer()
-- Tests that we correctly handle a call into an already-finalized object.
-- Collectible objects are finalized in the opposite order of creation.
do
local t = {}
defer(function()
assert_error_match("called into dead object", function()
-- Generic def call.
t[1]:lookup_msg("abc")
end)
end)
t = {
upb.DefPool(),
}
end
collectgarbage()
end
-- in-range of 64-bit types but not exactly representable as double
local bad64 = 2^68 - 1
local numeric_types = {
[upb.TYPE_UINT32] = {
valid_val = 2^32 - 1,
too_big = 2^32,
too_small = -1,
other_bad = 5.1
},
[upb.TYPE_UINT64] = {
valid_val = 2^63,
too_big = 2^64,
too_small = -1,
other_bad = bad64
},
[upb.TYPE_INT32] = {
valid_val = 2^31 - 1,
too_big = 2^31,
too_small = -2^31 - 1,
other_bad = 5.1
},
-- Enums don't exist at a language level in Lua, so we just represent enum
-- values as int32s.
[upb.TYPE_ENUM] = {
valid_val = 2^31 - 1,
too_big = 2^31,
too_small = -2^31 - 1,
other_bad = 5.1
},
[upb.TYPE_INT64] = {
valid_val = 2^62,
too_big = 2^63,
too_small = -2^64,
other_bad = bad64
},
[upb.TYPE_FLOAT] = {
valid_val = 340282306073709652508363335590014353408
},
[upb.TYPE_DOUBLE] = {
valid_val = 10^101
},
}
function test_utf8()
local invalid_utf8 = "\xff"
local proto2_msg = test_messages_proto2.TestAllTypesProto2{
optional_string = invalid_utf8,
}
-- As proto2, invalid UTF-8 parses and serializes fine.
local serialized = upb.encode(proto2_msg)
local proto2_msg2 = upb.decode(test_messages_proto2.TestAllTypesProto2, serialized)
-- Decoding as proto3 fails.
assert_error(function()
upb.decode(test_messages_proto3.TestAllTypesProto3, serialized)
end)
end
function test_msg_primitives()
local msg = test_messages_proto3.TestAllTypesProto3{
optional_int32 = 10,
optional_uint32 = 20,
optional_int64 = 30,
optional_uint64 = 40,
optional_double = 50,
optional_float = 60,
optional_sint32 = 70,
optional_sint64 = 80,
optional_fixed32 = 90,
optional_fixed64 = 100,
optional_sfixed32 = 110,
optional_sfixed64 = 120,
optional_bool = true,
optional_string = "abc",
optional_nested_message = test_messages_proto3['TestAllTypesProto3.NestedMessage']{a = 123},
}
-- Attempts to access non-existent fields fail.
assert_error_match("no such field", function() msg.no_such = 1 end)
assert_equal(10, msg.optional_int32)
assert_equal(20, msg.optional_uint32)
assert_equal(30, msg.optional_int64)
assert_equal(40, msg.optional_uint64)
assert_equal(50, msg.optional_double)
assert_equal(60, msg.optional_float)
assert_equal(70, msg.optional_sint32)
assert_equal(80, msg.optional_sint64)
assert_equal(90, msg.optional_fixed32)
assert_equal(100, msg.optional_fixed64)
assert_equal(110, msg.optional_sfixed32)
assert_equal(120, msg.optional_sfixed64)
assert_equal(true, msg.optional_bool)
assert_equal("abc", msg.optional_string)
assert_equal(123, msg.optional_nested_message.a)
end
function test_string_array()
local function test_for_string_type(upb_type)
local array = upb.Array(upb_type)
assert_equal(0, #array)
-- 0 is never a valid index in Lua.
assert_error_match("array index", function() return array[0] end)
-- Past the end of the array.
assert_error_match("array index", function() return array[1] end)
array[1] = "foo"
assert_equal("foo", array[1])
assert_equal(1, #array)
-- Past the end of the array.
assert_error_match("array index", function() return array[2] end)
local array2 = upb.Array(upb_type)
assert_equal(0, #array2)
array[2] = "bar"
assert_equal("foo", array[1])
assert_equal("bar", array[2])
assert_equal(2, #array)
-- Past the end of the array.
assert_error_match("array index", function() return array[3] end)
-- Can't assign other Lua types.
assert_error_match("Expected string", function() array[3] = 123 end)
assert_error_match("Expected string", function() array[3] = true end)
assert_error_match("Expected string", function() array[3] = false end)
assert_error_match("Expected string", function() array[3] = nil end)
assert_error_match("Expected string", function() array[3] = {} end)
assert_error_match("Expected string", function() array[3] = print end)
assert_error_match("Expected string", function() array[3] = array end)
end
test_for_string_type(upb.TYPE_STRING)
test_for_string_type(upb.TYPE_BYTES)
end
function test_numeric_array()
local function test_for_numeric_type(upb_type)
local array = upb.Array(upb_type)
local vals = numeric_types[upb_type]
assert_equal(0, #array)
-- 0 is never a valid index in Lua.
assert_error_match("array index", function() return array[0] end)
-- Past the end of the array.
assert_error_match("array index", function() return array[1] end)
array[1] = vals.valid_val
assert_equal(vals.valid_val, array[1])
assert_equal(1, #array)
assert_equal(vals.valid_val, array[1])
-- Past the end of the array.
assert_error_match("array index", function() return array[2] end)
array[2] = 10
assert_equal(vals.valid_val, array[1])
assert_equal(10, array[2])
assert_equal(2, #array)
-- Past the end of the array.
assert_error_match("array index", function() return array[3] end)
-- Values that are out of range.
local errmsg = "not an integer or out of range"
if vals.too_small then
assert_error_match(errmsg, function() array[3] = vals.too_small end)
end
if vals.too_big then
assert_error_match(errmsg, function() array[3] = vals.too_big end)
end
if vals.other_bad then
assert_error_match(errmsg, function() array[3] = vals.other_bad end)
end
-- Can't assign other Lua types.
errmsg = "bad argument #3"
assert_error_match(errmsg, function() array[3] = "abc" end)
assert_error_match(errmsg, function() array[3] = true end)
assert_error_match(errmsg, function() array[3] = false end)
assert_error_match(errmsg, function() array[3] = nil end)
assert_error_match(errmsg, function() array[3] = {} end)
assert_error_match(errmsg, function() array[3] = print end)
assert_error_match(errmsg, function() array[3] = array end)
end
for k in pairs(numeric_types) do
test_for_numeric_type(k)
end
end
function test_numeric_map()
local function test_for_numeric_types(key_type, val_type)
local map = upb.Map(key_type, val_type)
local key_vals = numeric_types[key_type]
local val_vals = numeric_types[val_type]
assert_equal(0, #map)
-- Unset keys return nil
assert_nil(map[key_vals.valid_val])
map[key_vals.valid_val] = val_vals.valid_val
assert_equal(1, #map)
assert_equal(val_vals.valid_val, map[key_vals.valid_val])
i = 0
for k, v in pairs(map) do
assert_equal(key_vals.valid_val, k)
assert_equal(val_vals.valid_val, v)
end
-- Out of range key/val
local errmsg = "not an integer or out of range"
if key_vals.too_small then
assert_error_match(errmsg, function() map[key_vals.too_small] = 1 end)
end
if key_vals.too_big then
assert_error_match(errmsg, function() map[key_vals.too_big] = 1 end)
end
if key_vals.other_bad then
assert_error_match(errmsg, function() map[key_vals.other_bad] = 1 end)
end
if val_vals.too_small then
assert_error_match(errmsg, function() map[1] = val_vals.too_small end)
end
if val_vals.too_big then
assert_error_match(errmsg, function() map[1] = val_vals.too_big end)
end
if val_vals.other_bad then
assert_error_match(errmsg, function() map[1] = val_vals.other_bad end)
end
end
for k in pairs(numeric_types) do
for v in pairs(numeric_types) do
test_for_numeric_types(k, v)
end
end
end
function test_unknown()
local bytes = string.rep("\x38\x00", 1000)
for i=1,1000 do
local msg = upb.decode(test_messages_proto3.TestAllTypesProto3, bytes)
end
end
function test_foo()
local defpool = upb.DefPool()
local filename = "external/com_google_protobuf/src/google/protobuf/descriptor_proto-descriptor-set.proto.bin"
local alternate_filename = "src/google/protobuf/descriptor_proto-descriptor-set.proto.bin"
local file = io.open(filename, "rb") or io.open("bazel-bin/" .. filename, "rb") or io.open(alternate_filename, "rb")
assert_not_nil(file)
local descriptor = file:read("*a")
assert_true(#descriptor > 0)
defpool:add_set(descriptor)
local FileDescriptorSet = defpool:lookup_msg("google.protobuf.FileDescriptorSet")
assert_not_nil(FileDescriptorSet)
set = FileDescriptorSet()
assert_equal(#set.file, 0)
assert_error_match("lupb.array expected", function () set.file = 1 end)
set = upb.decode(FileDescriptorSet, descriptor)
-- Test that we can at least call this without crashing.
set_textformat = tostring(set)
-- print(set_textformat)
assert_equal(#set.file, 1)
assert_equal(set.file[1].name, "google/protobuf/descriptor.proto")
end
function test_descriptor()
local defpool = upb.DefPool()
local file_proto = descriptor.FileDescriptorProto {
name = "test.proto",
message_type = upb.Array(descriptor.DescriptorProto, {
descriptor.DescriptorProto{
name = "ABC",
},
})
}
local file = defpool:add_file(upb.encode(file_proto))
assert_equal(file:defpool(), defpool)
end
function test_descriptor_error()
local defpool = upb.DefPool()
local file = descriptor.FileDescriptorProto()
file.name = "test.proto"
file.message_type[1] = descriptor.DescriptorProto{
name = "ABC"
}
file.message_type[2] = descriptor.DescriptorProto{
name = "BC."
}
assert_error(function () defpool:add_file(upb.encode(file)) end)
assert_nil(defpool:lookup_msg("ABC"))
end
function test_duplicate_enumval()
local defpool = upb.DefPool()
local file_proto = descriptor.FileDescriptorProto {
name = "test.proto",
message_type = upb.Array(descriptor.DescriptorProto, {
descriptor.DescriptorProto{
name = "ABC",
},
}),
enum_type = upb.Array(descriptor.EnumDescriptorProto, {
descriptor.EnumDescriptorProto{
name = "MyEnum",
value = upb.Array(descriptor.EnumValueDescriptorProto, {
descriptor.EnumValueDescriptorProto{
name = "ABC",
number = 1,
}
}),
},
})
}
assert_error(function () defpool:add_file(upb.encode(file_proto)) end)
end
function test_duplicate_filename_error()
local defpool = upb.DefPool()
local file = descriptor.FileDescriptorProto()
file.name = "test.proto"
defpool:add_file(upb.encode(file))
-- Second add with the same filename fails.
assert_error(function () defpool:add_file(upb.encode(file)) end)
end
function test_encode_skipunknown()
-- Test that upb.ENCODE_SKIPUNKNOWN does not encode unknown fields.
local msg = test_messages_proto3.TestAllTypesProto3{
optional_int32 = 10,
optional_uint32 = 20,
optional_int64 = 30,
}
-- SKIPUNKNOWN here tests that it does *not* affect regular fields.
local serialized = upb.encode(msg, {upb.ENCODE_SKIPUNKNOWN})
assert_true(#serialized > 0)
local empty_with_unknown = upb.decode(empty.Empty, serialized)
assert_true(#upb.encode(empty_with_unknown) > 0)
-- Verify that unknown fields are not serialized.
assert_true(#upb.encode(empty_with_unknown, {upb.ENCODE_SKIPUNKNOWN}) == 0)
end
function test_json_emit_defaults()
local msg = test_messages_proto3.TestAllTypesProto3()
local json = upb.json_encode(msg, {upb.JSONENC_EMITDEFAULTS})
end
function test_json_locale()
local msg = test_messages_proto3.TestAllTypesProto3()
msg.optional_double = 1.1
local original_locale = os.setlocale(nil)
os.setlocale("C")
local json = upb.json_encode(msg)
os.setlocale("de_DE.utf8")
assert_equal(json, upb.json_encode(msg))
os.setlocale(original_locale) -- Restore.
end
function test_encode_depth_limit()
local msg = test_messages_proto3.TestAllTypesProto3()
msg.recursive_message = msg
assert_error(function() upb.encode(msg) end)
end
function test_large_field_number()
local msg = upb_test.TestLargeFieldNumber()
msg.i32 = 5
local serialized = upb.encode(msg)
local msg2 = upb.decode(upb_test.TestLargeFieldNumber, serialized)
assert_equal(msg.i32, msg2.i32)
end
function test_timestamp_minutes()
local msg = upb.json_decode(upb_test.TestTimestamp, '{"ts": "2000-01-01T00:00:00-06:59"}')
assert_equal(msg.ts.seconds, 946684800 + ((6 * 60) + 59) * 60)
end
function test_gc()
local top = test_messages_proto3.TestAllTypesProto3()
local n = 100
local m
for i=1,n do
local inner = test_messages_proto3.TestAllTypesProto3()
m = inner
for j=1,n do
local tmp = m
m = test_messages_proto3.TestAllTypesProto3()
-- This will cause the arenas to fuse. But we stop referring to the child,
-- so the Lua object is eligible for collection (and therefore its original
-- arena can be collected too). Only the fusing will keep the C mem alivd.
m.recursive_message = tmp
end
top.recursive_message = m
end
collectgarbage()
for i=1,n do
-- Verify we can touch all the messages again and without accessing freed
-- memory.
m = m.recursive_message
assert_not_nil(m)
end
end
function test_b9440()
local m = upb_test.HelloRequest()
m.id = 8
assert_equal(8, m.id)
m.version = "1"
assert_equal(8, m.id)
end
local stats = lunit.main()
if stats.failed > 0 or stats.errors > 0 then
error("One or more errors in test suite")
end

@ -0,0 +1,261 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*
* require("lua") -- A Lua extension for upb.
*
* Exposes only the core library
* (sub-libraries are exposed in other extensions).
*
* 64-bit woes: Lua can only represent numbers of type lua_Number (which is
* double unless the user specifically overrides this). Doubles can represent
* the entire range of 64-bit integers, but lose precision once the integers are
* greater than 2^53.
*
* Lua 5.3 is adding support for integers, which will allow for 64-bit
* integers (which can be interpreted as signed or unsigned).
*
* LuaJIT supports 64-bit signed and unsigned boxed representations
* through its "cdata" mechanism, but this is not portable to regular Lua.
*
* Hopefully Lua 5.3 will come soon enough that we can either use Lua 5.3
* integer support or LuaJIT 64-bit cdata for users that need the entire
* domain of [u]int64 values.
*/
#include "lua/upb.h"
#include <float.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "lauxlib.h"
#include "upb/message/message.h"
/* Lua compatibility code *****************************************************/
/* Shims for upcoming Lua 5.3 functionality. */
static bool lua_isinteger(lua_State* L, int argn) {
LUPB_UNUSED(L);
LUPB_UNUSED(argn);
return false;
}
/* Utility functions **********************************************************/
void lupb_checkstatus(lua_State* L, upb_Status* s) {
if (!upb_Status_IsOk(s)) {
lua_pushstring(L, upb_Status_ErrorMessage(s));
lua_error(L);
}
}
/* Pushes a new userdata with the given metatable. */
void* lupb_newuserdata(lua_State* L, size_t size, int n, const char* type) {
#if LUA_VERSION_NUM >= 504
void* ret = lua_newuserdatauv(L, size, n);
#else
void* ret = lua_newuserdata(L, size);
lua_createtable(L, 0, n);
lua_setuservalue(L, -2);
#endif
/* Set metatable. */
luaL_getmetatable(L, type);
assert(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
lua_setmetatable(L, -2);
return ret;
}
#if LUA_VERSION_NUM < 504
int lua_setiuservalue(lua_State* L, int index, int n) {
lua_getuservalue(L, index);
lua_insert(L, -2);
lua_rawseti(L, -2, n);
lua_pop(L, 1);
return 1;
}
int lua_getiuservalue(lua_State* L, int index, int n) {
lua_getuservalue(L, index);
lua_rawgeti(L, -1, n);
lua_replace(L, -2);
return 1;
}
#endif
/* We use this function as the __index metamethod when a type has both methods
* and an __index metamethod. */
int lupb_indexmm(lua_State* L) {
/* Look up in __index table (which is a closure param). */
lua_pushvalue(L, 2);
lua_rawget(L, lua_upvalueindex(1));
if (!lua_isnil(L, -1)) {
return 1;
}
/* Not found, chain to user __index metamethod. */
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_call(L, 2, 1);
return 1;
}
void lupb_register_type(lua_State* L, const char* name, const luaL_Reg* m,
const luaL_Reg* mm) {
luaL_newmetatable(L, name);
if (mm) {
lupb_setfuncs(L, mm);
}
if (m) {
lua_createtable(L, 0, 0); /* __index table */
lupb_setfuncs(L, m);
/* Methods go in the mt's __index slot. If the user also specified an
* __index metamethod, use our custom lupb_indexmm() that can check both. */
lua_getfield(L, -2, "__index");
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
} else {
lua_pushcclosure(L, &lupb_indexmm, 2);
}
lua_setfield(L, -2, "__index");
}
lua_pop(L, 1); /* The mt. */
}
/* Scalar type mapping ********************************************************/
/* Functions that convert scalar/primitive values (numbers, strings, bool)
* between Lua and C/upb. Handles type/range checking. */
bool lupb_checkbool(lua_State* L, int narg) {
if (!lua_isboolean(L, narg)) {
luaL_error(L, "must be true or false");
}
return lua_toboolean(L, narg);
}
/* Unlike luaL_checkstring(), this does not allow implicit conversion to
* string. */
const char* lupb_checkstring(lua_State* L, int narg, size_t* len) {
if (lua_type(L, narg) != LUA_TSTRING) {
luaL_error(L, "Expected string");
}
return lua_tolstring(L, narg, len);
}
/* Unlike luaL_checkinteger, these do not implicitly convert from string or
* round an existing double value. We allow floating-point input, but only if
* the actual value is integral. */
#define INTCHECK(type, ctype, min, max) \
ctype lupb_check##type(lua_State* L, int narg) { \
double n; \
if (lua_isinteger(L, narg)) { \
return lua_tointeger(L, narg); \
} \
\
/* Prevent implicit conversion from string. */ \
luaL_checktype(L, narg, LUA_TNUMBER); \
n = lua_tonumber(L, narg); \
\
/* Check this double has no fractional part and remains in bounds. \
* Consider INT64_MIN and INT64_MAX: \
* 1. INT64_MIN -(2^63) is a power of 2, so this converts to a double. \
* 2. INT64_MAX (2^63 - 1) is not a power of 2, and conversion of \
* out-of-range integer values to a double can lead to undefined behavior. \
* On some compilers, this conversion can return 0, but it also can return \
* the max value. To deal with this, we can first divide by 2 to prevent \
* the overflow, multiply it back, and add 1 to find the true limit. */ \
double i; \
double max_value = (((double)max / 2) * 2) + 1; \
if ((modf(n, &i) != 0.0) || n < min || n >= max_value) { \
luaL_error(L, "number %f was not an integer or out of range for " #type, \
n); \
} \
return (ctype)n; \
} \
void lupb_push##type(lua_State* L, ctype val) { \
/* TODO: push integer for Lua >= 5.3, 64-bit cdata for LuaJIT. */ \
/* This is lossy for some [u]int64 values, which isn't great, but */ \
/* crashing when we encounter these values seems worse. */ \
lua_pushnumber(L, val); \
}
INTCHECK(int64, int64_t, INT64_MIN, INT64_MAX)
INTCHECK(int32, int32_t, INT32_MIN, INT32_MAX)
INTCHECK(uint64, uint64_t, 0, UINT64_MAX)
INTCHECK(uint32, uint32_t, 0, UINT32_MAX)
double lupb_checkdouble(lua_State* L, int narg) {
/* If we were being really hard-nosed here, we'd check whether the input was
* an integer that has no precise double representation. But doubles aren't
* generally expected to be exact like integers are, and worse this could
* cause data-dependent runtime errors: one run of the program could work fine
* because the integer calculations happened to be exactly representable in
* double, while the next could crash because of subtly different input. */
luaL_checktype(L, narg, LUA_TNUMBER); /* lua_tonumber() auto-converts. */
return lua_tonumber(L, narg);
}
float lupb_checkfloat(lua_State* L, int narg) {
/* We don't worry about checking whether the input can be exactly converted to
* float -- see above. */
luaL_checktype(L, narg, LUA_TNUMBER); /* lua_tonumber() auto-converts. */
return lua_tonumber(L, narg);
}
void lupb_pushdouble(lua_State* L, double d) { lua_pushnumber(L, d); }
void lupb_pushfloat(lua_State* L, float d) { lua_pushnumber(L, d); }
/* Library entry point ********************************************************/
int luaopen_lupb(lua_State* L) {
#if LUA_VERSION_NUM == 501
const struct luaL_Reg funcs[] = {{NULL, NULL}};
luaL_register(L, "upb_c", funcs);
#else
lua_createtable(L, 0, 8);
#endif
lupb_def_registertypes(L);
lupb_msg_registertypes(L);
return 1; /* Return package table. */
}

@ -0,0 +1,135 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*
* Shared definitions for upb Lua modules.
*/
#ifndef UPB_LUA_UPB_H_
#define UPB_LUA_UPB_H_
#include "lauxlib.h"
#include "upb/message/message.h"
#include "upb/reflection/def.h"
#include "upb/reflection/message.h"
/* Lua changes its API in incompatible ways in every minor release.
* This is some shim code to paper over the differences. */
#if LUA_VERSION_NUM == 501
#define lua_rawlen lua_objlen
#define lua_setuservalue(L, idx) lua_setfenv(L, idx)
#define lua_getuservalue(L, idx) lua_getfenv(L, idx)
#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 504
#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
#else
#error Only Lua 5.1-5.4 are supported
#endif
/* Create a new userdata with the given type and |n| uservals, which are popped
* from the stack to initialize the userdata. */
void* lupb_newuserdata(lua_State* L, size_t size, int n, const char* type);
#if LUA_VERSION_NUM < 504
/* Polyfills for this Lua 5.4 function. Pushes userval |n| for the userdata at
* |index|. */
int lua_setiuservalue(lua_State* L, int index, int n);
int lua_getiuservalue(lua_State* L, int index, int n);
#endif
/* Registers a type with the given name, methods, and metamethods. */
void lupb_register_type(lua_State* L, const char* name, const luaL_Reg* m,
const luaL_Reg* mm);
/* Checks the given upb_Status and throws a Lua error if it is not ok. */
void lupb_checkstatus(lua_State* L, upb_Status* s);
int luaopen_lupb(lua_State* L);
/* C <-> Lua value conversions. ***********************************************/
/* Custom check/push functions. Unlike the Lua equivalents, they are pinned to
* specific C types (instead of lua_Number, etc), and do not allow any implicit
* conversion or data loss. */
int64_t lupb_checkint64(lua_State* L, int narg);
int32_t lupb_checkint32(lua_State* L, int narg);
uint64_t lupb_checkuint64(lua_State* L, int narg);
uint32_t lupb_checkuint32(lua_State* L, int narg);
double lupb_checkdouble(lua_State* L, int narg);
float lupb_checkfloat(lua_State* L, int narg);
bool lupb_checkbool(lua_State* L, int narg);
const char* lupb_checkstring(lua_State* L, int narg, size_t* len);
const char* lupb_checkname(lua_State* L, int narg);
void lupb_pushint64(lua_State* L, int64_t val);
void lupb_pushint32(lua_State* L, int32_t val);
void lupb_pushuint64(lua_State* L, uint64_t val);
void lupb_pushuint32(lua_State* L, uint32_t val);
/** From def.c. ***************************************************************/
const upb_MessageDef* lupb_MessageDef_check(lua_State* L, int narg);
const upb_EnumDef* lupb_EnumDef_check(lua_State* L, int narg);
const upb_FieldDef* lupb_FieldDef_check(lua_State* L, int narg);
upb_DefPool* lupb_DefPool_check(lua_State* L, int narg);
void lupb_MessageDef_pushsubmsgdef(lua_State* L, const upb_FieldDef* f);
void lupb_def_registertypes(lua_State* L);
/** From msg.c. ***************************************************************/
void lupb_pushmsgval(lua_State* L, int container, upb_CType type,
upb_MessageValue val);
int lupb_MessageDef_call(lua_State* L);
upb_Arena* lupb_Arena_pushnew(lua_State* L);
void lupb_msg_registertypes(lua_State* L);
#define lupb_assert(L, predicate) \
if (!(predicate)) \
luaL_error(L, "internal error: %s, %s:%d ", #predicate, __FILE__, __LINE__);
#define LUPB_UNUSED(var) (void)var
#if defined(__GNUC__) || defined(__clang__)
#define LUPB_UNREACHABLE() \
do { \
assert(0); \
__builtin_unreachable(); \
} while (0)
#else
#define LUPB_UNREACHABLE() \
do { \
assert(0); \
} while (0)
#endif
#endif /* UPB_LUA_UPB_H_ */

@ -0,0 +1,58 @@
--[[--------------------------------------------------------------------------
Copyright (c) 2009-2021, Google LLC
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Google LLC nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--]]--------------------------------------------------------------------------
local upb = require("lupb")
upb.generated_pool = upb.DefPool()
local module_metatable = {
__index = function(t, k)
local package = t._filedef:package()
if package then
k = package .. "." .. k
end
local pool = upb.generated_pool
local def = pool:lookup_msg(k) or pool:lookup_enum(k)
local v = nil
if def and def:file():name() == t._filedef:name() then
v = def
t[k] = v
end
return v
end
}
function upb._generated_module(desc_string)
local file = upb.generated_pool:add_file(desc_string)
local module = {_filedef = file}
setmetatable(module, module_metatable)
return module
end
return upb

@ -0,0 +1,139 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/descriptor.pb.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/plugin.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/printer.h"
namespace protoc = ::google::protobuf::compiler;
namespace protobuf = ::google::protobuf;
class LuaGenerator : public protoc::CodeGenerator {
bool Generate(const protobuf::FileDescriptor* file,
const std::string& parameter, protoc::GeneratorContext* context,
std::string* error) const override;
};
static std::string StripExtension(absl::string_view fname) {
size_t lastdot = fname.find_last_of('.');
if (lastdot == std::string::npos) {
return std::string(fname);
}
return std::string(fname.substr(0, lastdot));
}
static std::string Filename(const protobuf::FileDescriptor* file) {
return StripExtension(file->name()) + "_pb.lua";
}
static std::string ModuleName(const protobuf::FileDescriptor* file) {
std::string ret = StripExtension(file->name()) + "_pb";
return absl::StrReplaceAll(ret, {{"/", "."}});
}
static void PrintHexDigit(char digit, protobuf::io::Printer* printer) {
char text;
if (digit < 10) {
text = '0' + digit;
} else {
text = 'A' + (digit - 10);
}
printer->WriteRaw(&text, 1);
}
static void PrintString(int max_cols, absl::string_view* str,
protobuf::io::Printer* printer) {
printer->Print("\'");
while (max_cols > 0 && !str->empty()) {
char ch = (*str)[0];
if (ch == '\\') {
printer->PrintRaw("\\\\");
max_cols--;
} else if (ch == '\'') {
printer->PrintRaw("\\'");
max_cols--;
} else if (isprint(ch)) {
printer->WriteRaw(&ch, 1);
max_cols--;
} else {
unsigned char byte = ch;
printer->PrintRaw("\\x");
PrintHexDigit(byte >> 4, printer);
PrintHexDigit(byte & 15, printer);
max_cols -= 4;
}
str->remove_prefix(1);
}
printer->Print("\'");
}
bool LuaGenerator::Generate(const protobuf::FileDescriptor* file,
const std::string& /* parameter */,
protoc::GeneratorContext* context,
std::string* /* error */) const {
std::string filename = Filename(file);
protobuf::io::ZeroCopyOutputStream* out = context->Open(filename);
protobuf::io::Printer printer(out, '$');
for (int i = 0; i < file->dependency_count(); i++) {
const protobuf::FileDescriptor* dep = file->dependency(i);
printer.Print("require('$name$')\n", "name", ModuleName(dep));
}
printer.Print("local upb = require('upb')\n");
protobuf::FileDescriptorProto file_proto;
file->CopyTo(&file_proto);
std::string file_data;
file_proto.SerializeToString(&file_data);
printer.Print("local descriptor = table.concat({\n");
absl::string_view data(file_data);
while (!data.empty()) {
printer.Print(" ");
PrintString(72, &data, &printer);
printer.Print(",\n");
}
printer.Print("})\n");
printer.Print("return upb._generated_module(descriptor)\n");
return true;
}
int main(int argc, char** argv) {
LuaGenerator generator;
return google::protobuf::compiler::PluginMain(argc, argv, &generator);
}

@ -0,0 +1,188 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load(
"//bazel:build_defs.bzl",
"UPB_DEFAULT_CPPOPTS",
)
load(
"//protos/bazel:upb_cc_proto_library.bzl",
"upb_cc_proto_library_copts",
)
# begin:google_only
# package(default_applicable_licenses = ["//:license"])
# end:google_only
licenses(["notice"])
cc_library(
name = "repeated_field",
hdrs = [
"repeated_field.h",
"repeated_field_iterator.h",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":protos",
":protos_traits",
"//:base",
"//:collections",
"//:collections_internal",
"//:mem",
"//:message_copy",
"//:mini_table",
"//:port",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "protos",
srcs = [
"protos.cc",
],
hdrs = [
"protos.h",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":protos_extension_lock",
"//:base",
"//:mem",
"//:message",
"//:message_copy",
"//:message_internal",
"//:message_promote",
"//:mini_table",
"//:wire",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
# Internally used type traits.
cc_library(
name = "protos_traits",
hdrs = [
"protos_traits.h",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:private"],
)
cc_library(
name = "protos_internal",
hdrs = ["protos_internal.h"],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":protos",
"//:mem",
"//:message",
"//:mini_table",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings:str_format",
],
)
cc_library(
name = "protos_extension_lock",
srcs = ["protos_extension_lock.cc"],
hdrs = ["protos_extension_lock.h"],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
"//:message",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/synchronization",
],
)
# Common support code for C++ generated code.
cc_library(
name = "generated_protos_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
hdrs = [
"protos_internal.h",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":protos",
":protos_internal",
":repeated_field",
"//:mem",
"//:message",
],
)
cc_test(
name = "protos_internal_test",
srcs = ["protos_internal_test.cc"],
copts = UPB_DEFAULT_CPPOPTS,
deps = [
":protos_internal",
"//:mem",
"//protos_generator/tests:test_model_upb_cc_proto",
"//protos_generator/tests:test_model_upb_proto",
"@com_google_googletest//:gtest_main",
],
)
upb_cc_proto_library_copts(
name = "upb_cc_proto_library_copts__for_generated_code_only_do_not_use",
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
)
cc_test(
name = "repeated_field_iterator_test",
srcs = ["repeated_field_iterator_test.cc"],
deps = [
":repeated_field",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "protos_extension_lock_test",
srcs = ["protos_extension_lock_test.cc"],
deps = [
"//:mem",
"//protos",
"//protos:protos_extension_lock",
"//protos_generator/tests:test_model_upb_cc_proto",
"@com_google_absl//absl/hash",
"@com_google_absl//absl/log:absl_check",
"@com_google_googletest//:gtest_main",
],
)

@ -0,0 +1,43 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
# begin:google_only
# package(default_applicable_licenses = ["//:license"])
# end:google_only
licenses(["notice"])
bzl_library(
name = "upb_cc_proto_library_bzl",
srcs = ["upb_cc_proto_library.bzl"],
visibility = ["//visibility:public"],
deps = [
"@bazel_skylib//lib:paths",
"//bazel:upb_proto_library_bzl",
"@bazel_tools//tools/cpp:toolchain_utils.bzl",
],
)

@ -0,0 +1,307 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Public rules for using upb protos:
- upb_cc_proto_library()
"""
load("@bazel_skylib//lib:paths.bzl", "paths")
load("//bazel:upb_proto_library.bzl", "GeneratedSrcsInfo", "UpbWrappedCcInfo", "upb_proto_library_aspect")
# begin:google_only
# load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
#
# end:google_only
# begin:github_only
# Compatibility code for Bazel 4.x. Remove this when we drop support for Bazel 4.x.
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
def use_cpp_toolchain():
return ["@bazel_tools//tools/cpp:toolchain_type"]
# end:github_only
# Generic support code #########################################################
# begin:github_only
_is_google3 = False
# end:github_only
# begin:google_only
# _is_google3 = True
# end:google_only
def _get_real_short_path(file):
# For some reason, files from other archives have short paths that look like:
# ../com_google_protobuf/google/protobuf/descriptor.proto
short_path = file.short_path
if short_path.startswith("../"):
second_slash = short_path.index("/", 3)
short_path = short_path[second_slash + 1:]
# Sometimes it has another few prefixes like:
# _virtual_imports/any_proto/google/protobuf/any.proto
# benchmarks/_virtual_imports/100_msgs_proto/benchmarks/100_msgs.proto
# We want just google/protobuf/any.proto.
virtual_imports = "_virtual_imports/"
if virtual_imports in short_path:
short_path = short_path.split(virtual_imports)[1].split("/", 1)[1]
return short_path
def _get_real_root(file):
real_short_path = _get_real_short_path(file)
return file.path[:-len(real_short_path) - 1]
def _generate_output_file(ctx, src, extension):
real_short_path = _get_real_short_path(src)
real_short_path = paths.relativize(real_short_path, ctx.label.package)
output_filename = paths.replace_extension(real_short_path, extension)
ret = ctx.actions.declare_file(output_filename)
return ret
def _filter_none(elems):
out = []
for elem in elems:
if elem:
out.append(elem)
return out
def _cc_library_func(ctx, name, hdrs, srcs, copts, dep_ccinfos):
"""Like cc_library(), but callable from rules.
Args:
ctx: Rule context.
name: Unique name used to generate output files.
hdrs: Public headers that can be #included from other rules.
srcs: C/C++ source files.
copts: Additional options for cc compilation.
dep_ccinfos: CcInfo providers of dependencies we should build/link against.
Returns:
CcInfo provider for this compilation.
"""
compilation_contexts = [info.compilation_context for info in dep_ccinfos]
linking_contexts = [info.linking_context for info in dep_ccinfos]
toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
(compilation_context, compilation_outputs) = cc_common.compile(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = toolchain,
name = name,
srcs = srcs,
public_hdrs = hdrs,
user_compile_flags = copts,
compilation_contexts = compilation_contexts,
)
# buildifier: disable=unused-variable
(linking_context, linking_outputs) = cc_common.create_linking_context_from_compilation_outputs(
actions = ctx.actions,
name = name,
feature_configuration = feature_configuration,
cc_toolchain = toolchain,
compilation_outputs = compilation_outputs,
linking_contexts = linking_contexts,
)
return CcInfo(
compilation_context = compilation_context,
linking_context = linking_context,
)
# Dummy rule to expose select() copts to aspects ##############################
UpbCcProtoLibraryCoptsInfo = provider(
"Provides copts for upb cc proto targets",
fields = {
"copts": "copts for upb_cc_proto_library()",
},
)
def upb_cc_proto_library_copts_impl(ctx):
return UpbCcProtoLibraryCoptsInfo(copts = ctx.attr.copts)
upb_cc_proto_library_copts = rule(
implementation = upb_cc_proto_library_copts_impl,
attrs = {"copts": attr.string_list(default = [])},
)
_UpbCcWrappedCcInfo = provider("Provider for cc_info for protos", fields = ["cc_info"])
_WrappedCcGeneratedSrcsInfo = provider("Provider for generated sources", fields = ["srcs"])
def _compile_upb_cc_protos(ctx, generator, proto_info, proto_sources):
if len(proto_sources) == 0:
return GeneratedSrcsInfo(srcs = [], hdrs = [])
tool = getattr(ctx.executable, "_gen_" + generator)
srcs = [_generate_output_file(ctx, name, ".upb.proto.cc") for name in proto_sources]
hdrs = [_generate_output_file(ctx, name, ".upb.proto.h") for name in proto_sources]
hdrs += [_generate_output_file(ctx, name, ".upb.fwd.h") for name in proto_sources]
transitive_sets = proto_info.transitive_descriptor_sets.to_list()
args = ctx.actions.args()
args.use_param_file(param_file_arg = "@%s")
args.set_param_file_format("multiline")
args.add("--" + generator + "_out=" + _get_real_root(srcs[0]))
args.add("--plugin=protoc-gen-" + generator + "=" + tool.path)
args.add("--descriptor_set_in=" + ctx.configuration.host_path_separator.join([f.path for f in transitive_sets]))
args.add_all(proto_sources, map_each = _get_real_short_path)
ctx.actions.run(
inputs = depset(
direct = [proto_info.direct_descriptor_set],
transitive = [proto_info.transitive_descriptor_sets],
),
tools = [tool],
outputs = srcs + hdrs,
executable = ctx.executable._protoc,
arguments = [args],
progress_message = "Generating upb cc protos for :" + ctx.label.name,
)
return GeneratedSrcsInfo(srcs = srcs, hdrs = hdrs)
def _upb_cc_proto_rule_impl(ctx):
if len(ctx.attr.deps) != 1:
fail("only one deps dependency allowed.")
dep = ctx.attr.deps[0]
if _WrappedCcGeneratedSrcsInfo in dep:
srcs = dep[_WrappedCcGeneratedSrcsInfo].srcs
else:
fail("proto_library rule must generate _WrappedCcGeneratedSrcsInfo (aspect should have " +
"handled this).")
if _UpbCcWrappedCcInfo in dep:
cc_info = dep[_UpbCcWrappedCcInfo].cc_info
elif UpbWrappedCcInfo in dep:
cc_info = dep[UpbWrappedCcInfo].cc_info
else:
fail("proto_library rule must generate UpbWrappedCcInfo or " +
"_UpbCcWrappedCcInfo (aspect should have handled this).")
lib = cc_info.linking_context.linker_inputs.to_list()[0].libraries[0]
files = _filter_none([
lib.static_library,
lib.pic_static_library,
lib.dynamic_library,
])
return [
DefaultInfo(files = depset(files + srcs.hdrs + srcs.srcs)),
srcs,
cc_info,
]
def _upb_cc_proto_aspect_impl(target, ctx, generator, cc_provider, file_provider):
proto_info = target[ProtoInfo]
files = _compile_upb_cc_protos(ctx, generator, proto_info, proto_info.direct_sources)
deps = ctx.rule.attr.deps + getattr(ctx.attr, "_" + generator)
dep_ccinfos = [dep[CcInfo] for dep in deps if CcInfo in dep]
dep_ccinfos += [dep[UpbWrappedCcInfo].cc_info for dep in deps if UpbWrappedCcInfo in dep]
dep_ccinfos += [dep[_UpbCcWrappedCcInfo].cc_info for dep in deps if _UpbCcWrappedCcInfo in dep]
if UpbWrappedCcInfo not in target:
fail("Target should have UpbWrappedCcInfo provider")
dep_ccinfos.append(target[UpbWrappedCcInfo].cc_info)
cc_info = _cc_library_func(
ctx = ctx,
name = ctx.rule.attr.name + "." + generator,
hdrs = files.hdrs,
srcs = files.srcs,
copts = ctx.attr._ccopts[UpbCcProtoLibraryCoptsInfo].copts,
dep_ccinfos = dep_ccinfos,
)
return [cc_provider(cc_info = cc_info), file_provider(srcs = files)]
def _upb_cc_proto_library_aspect_impl(target, ctx):
return _upb_cc_proto_aspect_impl(target, ctx, "upbprotos", _UpbCcWrappedCcInfo, _WrappedCcGeneratedSrcsInfo)
_upb_cc_proto_library_aspect = aspect(
attrs = {
"_ccopts": attr.label(
default = "//protos:upb_cc_proto_library_copts__for_generated_code_only_do_not_use",
),
"_gen_upbprotos": attr.label(
executable = True,
cfg = "exec",
default = "//protos_generator:protoc-gen-upb-protos",
),
"_protoc": attr.label(
executable = True,
cfg = "exec",
default = "@com_google_protobuf//:protoc",
),
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"_upbprotos": attr.label_list(
default = [
# TODO: Add dependencies for cc runtime (absl/string etc..)
"//:generated_cpp_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
"//protos:generated_protos_support__only_for_generated_code_do_not_use__i_give_permission_to_break_me",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/status:statusor",
"//protos",
"//protos:repeated_field",
],
),
},
implementation = _upb_cc_proto_library_aspect_impl,
provides = [
_UpbCcWrappedCcInfo,
_WrappedCcGeneratedSrcsInfo,
],
required_aspect_providers = [
UpbWrappedCcInfo,
],
attr_aspects = ["deps"],
fragments = ["cpp"],
toolchains = use_cpp_toolchain(),
incompatible_use_toolchain_transition = True,
)
upb_cc_proto_library = rule(
output_to_genfiles = True,
implementation = _upb_cc_proto_rule_impl,
attrs = {
"deps": attr.label_list(
aspects = [
upb_proto_library_aspect,
_upb_cc_proto_library_aspect,
],
allow_rules = ["proto_library"],
providers = [ProtoInfo],
),
"_ccopts": attr.label(
default = "//protos:upb_cc_proto_library_copts__for_generated_code_only_do_not_use",
),
},
)

@ -0,0 +1,187 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos/protos.h"
#include <atomic>
#include <cstddef>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "protos/protos_extension_lock.h"
#include "upb/mem/arena.h"
#include "upb/message/copy.h"
#include "upb/message/internal/extension.h"
#include "upb/message/promote.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/extension_registry.h"
#include "upb/mini_table/message.h"
#include "upb/wire/decode.h"
#include "upb/wire/encode.h"
namespace protos {
// begin:google_only
// absl::Status MessageAllocationError(SourceLocation loc) {
// return absl::Status(absl::StatusCode::kInternal,
// "Upb message allocation error", loc);
// }
//
// absl::Status ExtensionNotFoundError(int extension_number, SourceLocation loc) {
// return absl::Status(
// absl::StatusCode::kInternal,
// absl::StrFormat("Extension %d not found", extension_number), loc);
// }
//
// absl::Status MessageEncodeError(upb_EncodeStatus status, SourceLocation loc) {
// return absl::Status(absl::StatusCode::kInternal,
// absl::StrFormat("Upb message encoding error %d", status),
// loc
//
// );
// }
//
// absl::Status MessageDecodeError(upb_DecodeStatus status, SourceLocation loc
//
// ) {
// return absl::Status(absl::StatusCode::kInternal,
// absl::StrFormat("Upb message parse error %d", status), loc
//
// );
// }
// end:google_only
// begin:github_only
absl::Status MessageAllocationError(SourceLocation loc) {
return absl::Status(absl::StatusCode::kUnknown,
"Upb message allocation error");
}
absl::Status ExtensionNotFoundError(int ext_number, SourceLocation loc) {
return absl::Status(absl::StatusCode::kUnknown,
absl::StrFormat("Extension %d not found", ext_number));
}
absl::Status MessageEncodeError(upb_EncodeStatus s, SourceLocation loc) {
return absl::Status(absl::StatusCode::kUnknown, "Encoding error");
}
absl::Status MessageDecodeError(upb_DecodeStatus status, SourceLocation loc
) {
return absl::Status(absl::StatusCode::kUnknown, "Upb message parse error");
}
// end:github_only
namespace internal {
upb_ExtensionRegistry* GetUpbExtensions(
const ExtensionRegistry& extension_registry) {
return extension_registry.registry_;
}
/**
* MessageLock(msg) acquires lock on msg when constructed and releases it when
* destroyed.
*/
class MessageLock {
public:
explicit MessageLock(const upb_Message* msg) : msg_(msg) {
UpbExtensionLocker locker =
upb_extension_locker_global.load(std::memory_order_acquire);
unlocker_ = (locker != nullptr) ? locker(msg) : nullptr;
}
MessageLock(const MessageLock&) = delete;
void operator=(const MessageLock&) = delete;
~MessageLock() {
if (unlocker_ != nullptr) {
unlocker_(msg_);
}
}
private:
const upb_Message* msg_;
UpbExtensionUnlocker unlocker_;
};
bool HasExtensionOrUnknown(const upb_Message* msg,
const upb_MiniTableExtension* eid) {
MessageLock msg_lock(msg);
return _upb_Message_Getext(msg, eid) != nullptr ||
upb_MiniTable_FindUnknown(msg, eid->field.number,
kUpb_WireFormat_DefaultDepthLimit)
.status == kUpb_FindUnknown_Ok;
}
const upb_Message_Extension* GetOrPromoteExtension(
upb_Message* msg, const upb_MiniTableExtension* eid, upb_Arena* arena) {
MessageLock msg_lock(msg);
const upb_Message_Extension* ext = _upb_Message_Getext(msg, eid);
if (ext == nullptr) {
upb_GetExtension_Status ext_status = upb_MiniTable_GetOrPromoteExtension(
(upb_Message*)msg, eid, kUpb_WireFormat_DefaultDepthLimit, arena, &ext);
if (ext_status != kUpb_GetExtension_Ok) {
ext = nullptr;
}
}
return ext;
}
absl::StatusOr<absl::string_view> Serialize(const upb_Message* message,
const upb_MiniTable* mini_table,
upb_Arena* arena, int options) {
MessageLock msg_lock(message);
size_t len;
char* ptr;
upb_EncodeStatus status =
upb_Encode(message, mini_table, options, arena, &ptr, &len);
if (status == kUpb_EncodeStatus_Ok) {
return absl::string_view(ptr, len);
}
return MessageEncodeError(status);
}
void DeepCopy(upb_Message* target, const upb_Message* source,
const upb_MiniTable* mini_table, upb_Arena* arena) {
MessageLock msg_lock(source);
upb_Message_DeepCopy(target, source, mini_table, arena);
}
upb_Message* DeepClone(const upb_Message* source,
const upb_MiniTable* mini_table, upb_Arena* arena) {
MessageLock msg_lock(source);
return upb_Message_DeepClone(source, mini_table, arena);
}
} // namespace internal
} // namespace protos

@ -0,0 +1,520 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_PROTOS_H_
#define UPB_PROTOS_PROTOS_H_
#include <type_traits>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "upb/base/status.hpp"
#include "upb/mem/arena.hpp"
#include "upb/message/copy.h"
#include "upb/message/internal/extension.h"
#include "upb/wire/decode.h"
#include "upb/wire/encode.h"
namespace protos {
using Arena = ::upb::Arena;
class ExtensionRegistry;
template <typename T>
using Proxy = std::conditional_t<std::is_const<T>::value,
typename std::remove_const_t<T>::CProxy,
typename T::Proxy>;
// Provides convenient access to Proxy and CProxy message types.
//
// Using rebinding and handling of const, Ptr<Message> and Ptr<const Message>
// allows copying const with T* const and avoids using non-copyable Proxy types
// directly.
template <typename T>
class Ptr final {
public:
Ptr() = delete;
// Implicit conversions
Ptr(T* m) : p_(m) {} // NOLINT
Ptr(const Proxy<T>* p) : p_(*p) {} // NOLINT
Ptr(Proxy<T> p) : p_(p) {} // NOLINT
Ptr(const Ptr& m) = default;
Ptr& operator=(Ptr v) & {
Proxy<T>::Rebind(p_, v.p_);
return *this;
}
Proxy<T> operator*() const { return p_; }
Proxy<T>* operator->() const {
return const_cast<Proxy<T>*>(std::addressof(p_));
}
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wclass-conversion"
#endif
template <typename U = T, std::enable_if_t<!std::is_const<U>::value, int> = 0>
operator Ptr<const T>() const {
Proxy<const T> p(p_);
return Ptr<const T>(&p);
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
private:
Ptr(void* msg, upb_Arena* arena) : p_(msg, arena) {} // NOLINT
friend class Ptr<const T>;
friend typename T::Access;
Proxy<T> p_;
};
namespace internal {
struct PrivateAccess {
template <typename T>
static auto* GetInternalMsg(T&& message) {
return message->msg();
}
};
template <typename T>
auto* GetInternalMsg(T&& message) {
return PrivateAccess::GetInternalMsg(std::forward<T>(message));
}
} // namespace internal
inline absl::string_view UpbStrToStringView(upb_StringView str) {
return absl::string_view(str.data, str.size);
}
// TODO: update bzl and move to upb runtime / protos.cc.
inline upb_StringView UpbStrFromStringView(absl::string_view str,
upb_Arena* arena) {
const size_t str_size = str.size();
char* buffer = static_cast<char*>(upb_Arena_Malloc(arena, str_size));
memcpy(buffer, str.data(), str_size);
return upb_StringView_FromDataAndSize(buffer, str_size);
}
template <typename T>
typename T::Proxy CreateMessage(::protos::Arena& arena) {
return typename T::Proxy(upb_Message_New(T::minitable(), arena.ptr()),
arena.ptr());
}
// begin:github_only
// This type exists to work around an absl type that has not yet been
// released.
struct SourceLocation {
static SourceLocation current() { return {}; }
absl::string_view file_name() { return "<unknown>"; }
int line() { return 0; }
};
// end:github_only
// begin:google_only
// using SourceLocation = absl::SourceLocation;
// end:google_only
absl::Status MessageAllocationError(
SourceLocation loc = SourceLocation::current());
absl::Status ExtensionNotFoundError(
int extension_number, SourceLocation loc = SourceLocation::current());
absl::Status MessageDecodeError(upb_DecodeStatus status,
SourceLocation loc = SourceLocation::current());
absl::Status MessageEncodeError(upb_EncodeStatus status,
SourceLocation loc = SourceLocation::current());
namespace internal {
template <typename T>
T CreateMessage() {
return T();
}
template <typename T>
typename T::Proxy CreateMessageProxy(void* msg, upb_Arena* arena) {
return typename T::Proxy(msg, arena);
}
template <typename T>
typename T::CProxy CreateMessage(upb_Message* msg, upb_Arena* arena) {
return typename T::CProxy(msg, arena);
}
class ExtensionMiniTableProvider {
public:
constexpr explicit ExtensionMiniTableProvider(
const upb_MiniTableExtension* mini_table_ext)
: mini_table_ext_(mini_table_ext) {}
const upb_MiniTableExtension* mini_table_ext() const {
return mini_table_ext_;
}
private:
const upb_MiniTableExtension* mini_table_ext_;
};
// -------------------------------------------------------------------
// ExtensionIdentifier
// This is the type of actual extension objects. E.g. if you have:
// extend Foo {
// optional MyExtension bar = 1234;
// }
// then "bar" will be defined in C++ as:
// ExtensionIdentifier<Foo, MyExtension> bar(&namespace_bar_ext);
template <typename ExtendeeType, typename ExtensionType>
class ExtensionIdentifier : public ExtensionMiniTableProvider {
public:
using Extension = ExtensionType;
using Extendee = ExtendeeType;
constexpr explicit ExtensionIdentifier(
const upb_MiniTableExtension* mini_table_ext)
: ExtensionMiniTableProvider(mini_table_ext) {}
};
template <typename T>
upb_Arena* GetArena(Ptr<T> message) {
return static_cast<upb_Arena*>(message->GetInternalArena());
}
template <typename T>
upb_Arena* GetArena(T* message) {
return static_cast<upb_Arena*>(message->GetInternalArena());
}
template <typename T>
const upb_MiniTable* GetMiniTable(const T*) {
return T::minitable();
}
template <typename T>
const upb_MiniTable* GetMiniTable(Ptr<T>) {
return T::minitable();
}
upb_ExtensionRegistry* GetUpbExtensions(
const ExtensionRegistry& extension_registry);
absl::StatusOr<absl::string_view> Serialize(const upb_Message* message,
const upb_MiniTable* mini_table,
upb_Arena* arena, int options);
bool HasExtensionOrUnknown(const upb_Message* msg,
const upb_MiniTableExtension* eid);
const upb_Message_Extension* GetOrPromoteExtension(
upb_Message* msg, const upb_MiniTableExtension* eid, upb_Arena* arena);
void DeepCopy(upb_Message* target, const upb_Message* source,
const upb_MiniTable* mini_table, upb_Arena* arena);
upb_Message* DeepClone(const upb_Message* source,
const upb_MiniTable* mini_table, upb_Arena* arena);
} // namespace internal
template <typename T>
void DeepCopy(Ptr<const T> source_message, Ptr<T> target_message) {
static_assert(!std::is_const_v<T>);
::protos::internal::DeepCopy(
internal::GetInternalMsg(target_message),
internal::GetInternalMsg(source_message), T::minitable(),
static_cast<upb_Arena*>(target_message->GetInternalArena()));
}
template <typename T>
typename T::Proxy CloneMessage(Ptr<T> message, upb::Arena& arena) {
return typename T::Proxy(
::protos::internal::DeepClone(internal::GetInternalMsg(message),
T::minitable(), arena.ptr()),
arena.ptr());
}
template <typename T>
void DeepCopy(Ptr<const T> source_message, T* target_message) {
static_assert(!std::is_const_v<T>);
DeepCopy(source_message, protos::Ptr(target_message));
}
template <typename T>
void DeepCopy(const T* source_message, Ptr<T> target_message) {
static_assert(!std::is_const_v<T>);
DeepCopy(protos::Ptr(source_message), target_message);
}
template <typename T>
void DeepCopy(const T* source_message, T* target_message) {
static_assert(!std::is_const_v<T>);
DeepCopy(protos::Ptr(source_message), protos::Ptr(target_message));
}
template <typename T>
void ClearMessage(Ptr<T> message) {
static_assert(!std::is_const_v<T>, "");
upb_Message_Clear(internal::GetInternalMsg(message), T::minitable());
}
template <typename T>
void ClearMessage(T* message) {
ClearMessage(protos::Ptr(message));
}
class ExtensionRegistry {
public:
ExtensionRegistry(
const std::vector<const ::protos::internal::ExtensionMiniTableProvider*>&
extensions,
const upb::Arena& arena)
: registry_(upb_ExtensionRegistry_New(arena.ptr())) {
if (registry_) {
for (const auto& ext_provider : extensions) {
const auto* ext = ext_provider->mini_table_ext();
bool success = upb_ExtensionRegistry_AddArray(registry_, &ext, 1);
if (!success) {
registry_ = nullptr;
break;
}
}
}
}
private:
friend upb_ExtensionRegistry* ::protos::internal::GetUpbExtensions(
const ExtensionRegistry& extension_registry);
upb_ExtensionRegistry* registry_;
};
template <typename T>
using EnableIfProtosClass = std::enable_if_t<
std::is_base_of<typename T::Access, T>::value &&
std::is_base_of<typename T::Access, typename T::ExtendableType>::value>;
template <typename T>
using EnableIfMutableProto = std::enable_if_t<!std::is_const<T>::value>;
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>>
ABSL_MUST_USE_RESULT bool HasExtension(
Ptr<T> message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
return ::protos::internal::HasExtensionOrUnknown(
::protos::internal::GetInternalMsg(message), id.mini_table_ext());
}
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>>
ABSL_MUST_USE_RESULT bool HasExtension(
const T* message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
return HasExtension(protos::Ptr(message), id);
}
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>, typename = EnableIfMutableProto<T>>
void ClearExtension(
Ptr<T> message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
static_assert(!std::is_const_v<T>, "");
_upb_Message_ClearExtensionField(internal::GetInternalMsg(message),
id.mini_table_ext());
}
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>>
void ClearExtension(
T* message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
ClearExtension(::protos::Ptr(message), id);
}
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>, typename = EnableIfMutableProto<T>>
absl::Status SetExtension(
Ptr<T> message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id,
Extension& value) {
static_assert(!std::is_const_v<T>);
auto* message_arena = static_cast<upb_Arena*>(message->GetInternalArena());
upb_Message_Extension* msg_ext = _upb_Message_GetOrCreateExtension(
internal::GetInternalMsg(message), id.mini_table_ext(), message_arena);
if (!msg_ext) {
return MessageAllocationError();
}
auto* extension_arena = static_cast<upb_Arena*>(message->GetInternalArena());
if (message_arena != extension_arena) {
upb_Arena_Fuse(message_arena, extension_arena);
}
msg_ext->data.ptr = internal::GetInternalMsg(&value);
return absl::OkStatus();
}
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>>
absl::Status SetExtension(
T* message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id,
Extension& value) {
return ::protos::SetExtension(::protos::Ptr(message), id, value);
}
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>>
absl::StatusOr<Ptr<const Extension>> GetExtension(
Ptr<T> message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
// TODO(b/294089233): Fix const correctness issues.
const upb_Message_Extension* ext = ::protos::internal::GetOrPromoteExtension(
const_cast<upb_Message*>(internal::GetInternalMsg(message)),
id.mini_table_ext(), ::protos::internal::GetArena(message));
if (!ext) {
return ExtensionNotFoundError(id.mini_table_ext()->field.number);
}
return Ptr<const Extension>(::protos::internal::CreateMessage<Extension>(
ext->data.ptr, ::protos::internal::GetArena(message)));
}
template <typename T, typename Extendee, typename Extension,
typename = EnableIfProtosClass<T>>
absl::StatusOr<Ptr<const Extension>> GetExtension(
const T* message,
const ::protos::internal::ExtensionIdentifier<Extendee, Extension>& id) {
return GetExtension(protos::Ptr(message), id);
}
template <typename T>
ABSL_MUST_USE_RESULT bool Parse(Ptr<T> message, absl::string_view bytes) {
static_assert(!std::is_const_v<T>);
upb_Message_Clear(internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message));
auto* arena = static_cast<upb_Arena*>(message->GetInternalArena());
return upb_Decode(bytes.data(), bytes.size(),
internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message),
/* extreg= */ nullptr, /* options= */ 0,
arena) == kUpb_DecodeStatus_Ok;
}
template <typename T>
ABSL_MUST_USE_RESULT bool Parse(
Ptr<T> message, absl::string_view bytes,
const ::protos::ExtensionRegistry& extension_registry) {
static_assert(!std::is_const_v<T>);
upb_Message_Clear(internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message));
auto* arena = static_cast<upb_Arena*>(message->GetInternalArena());
return upb_Decode(bytes.data(), bytes.size(),
internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message),
/* extreg= */
::protos::internal::GetUpbExtensions(extension_registry),
/* options= */ 0, arena) == kUpb_DecodeStatus_Ok;
}
template <typename T>
ABSL_MUST_USE_RESULT bool Parse(
T* message, absl::string_view bytes,
const ::protos::ExtensionRegistry& extension_registry) {
static_assert(!std::is_const_v<T>);
return Parse(protos::Ptr(message, bytes, extension_registry));
}
template <typename T>
ABSL_MUST_USE_RESULT bool Parse(T* message, absl::string_view bytes) {
static_assert(!std::is_const_v<T>);
upb_Message_Clear(internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message));
auto* arena = static_cast<upb_Arena*>(message->GetInternalArena());
return upb_Decode(bytes.data(), bytes.size(),
internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message),
/* extreg= */ nullptr, /* options= */ 0,
arena) == kUpb_DecodeStatus_Ok;
}
template <typename T>
absl::StatusOr<T> Parse(absl::string_view bytes, int options = 0) {
T message;
auto* arena = static_cast<upb_Arena*>(message.GetInternalArena());
upb_DecodeStatus status =
upb_Decode(bytes.data(), bytes.size(), message.msg(),
::protos::internal::GetMiniTable(&message),
/* extreg= */ nullptr, /* options= */ 0, arena);
if (status == kUpb_DecodeStatus_Ok) {
return message;
}
return MessageDecodeError(status);
}
template <typename T>
absl::StatusOr<T> Parse(absl::string_view bytes,
const ::protos::ExtensionRegistry& extension_registry,
int options = 0) {
T message;
auto* arena = static_cast<upb_Arena*>(message.GetInternalArena());
upb_DecodeStatus status =
upb_Decode(bytes.data(), bytes.size(), message.msg(),
::protos::internal::GetMiniTable(&message),
::protos::internal::GetUpbExtensions(extension_registry),
/* options= */ 0, arena);
if (status == kUpb_DecodeStatus_Ok) {
return message;
}
return MessageDecodeError(status);
}
template <typename T>
absl::StatusOr<absl::string_view> Serialize(const T* message, upb::Arena& arena,
int options = 0) {
return ::protos::internal::Serialize(
internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message), arena.ptr(), options);
}
template <typename T>
absl::StatusOr<absl::string_view> Serialize(Ptr<T> message, upb::Arena& arena,
int options = 0) {
return ::protos::internal::Serialize(
internal::GetInternalMsg(message),
::protos::internal::GetMiniTable(message), arena.ptr(), options);
}
} // namespace protos
#endif // UPB_PROTOS_PROTOS_H_

@ -0,0 +1,39 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos/protos_extension_lock.h"
#include <atomic>
namespace protos::internal {
std::atomic<UpbExtensionLocker> upb_extension_locker_global;
} // namespace protos::internal

@ -0,0 +1,54 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_PROTOS_EXTENSION_LOCK_H_
#define UPB_PROTOS_PROTOS_EXTENSION_LOCK_H_
#include <atomic>
namespace protos::internal {
// TODO(b/295355754): Temporary locking api for cross-language
// concurrency issue around extension api that uses lazy promotion
// from unknown data to upb_MiniTableExtension. Will be replaced by
// a core runtime solution in the future.
//
// Any api(s) using unknown or extension data (GetOrPromoteExtension,
// Serialize and others) call lock/unlock to provide a way for
// mixed language implementations to avoid race conditions)
using UpbExtensionUnlocker = void (*)(const void*);
using UpbExtensionLocker = UpbExtensionUnlocker (*)(const void*);
// TODO(b/295355754): Expose as function instead of global.
extern std::atomic<UpbExtensionLocker> upb_extension_locker_global;
} // namespace protos::internal
#endif // UPB_PROTOS_PROTOS_EXTENSION_LOCK_H_

@ -0,0 +1,140 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos/protos_extension_lock.h"
#include <atomic>
#include <mutex>
#include <string>
#include <thread>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/hash/hash.h"
#include "absl/log/absl_check.h"
#include "protos/protos.h"
#include "protos_generator/tests/test_model.upb.proto.h"
#include "upb/mem/arena.hpp"
#ifndef ASSERT_OK
#define ASSERT_OK(x) ASSERT_TRUE(x.ok())
#endif // ASSERT_OK
#ifndef EXPECT_OK
#define EXPECT_OK(x) EXPECT_TRUE(x.ok())
#endif // EXPECT_OK
namespace protos_generator::test::protos {
namespace {
std::string GenerateTestData() {
TestModel model;
model.set_str1("str");
ThemeExtension extension1;
extension1.set_ext_name("theme");
ABSL_CHECK_OK(::protos::SetExtension(&model, theme, extension1));
ThemeExtension extension2;
extension2.set_ext_name("theme_extension");
ABSL_CHECK_OK(::protos::SetExtension(&model, ThemeExtension::theme_extension,
extension2));
::upb::Arena arena;
auto bytes = ::protos::Serialize(&model, arena);
ABSL_CHECK_OK(bytes);
return std::string(bytes->data(), bytes->size());
}
std::mutex m[8];
void unlock_func(const void* msg) { m[absl::HashOf(msg) & 0x7].unlock(); }
::protos::internal::UpbExtensionUnlocker lock_func(const void* msg) {
m[absl::HashOf(msg) & 0x7].lock();
return &unlock_func;
}
void TestConcurrentExtensionAccess(::protos::ExtensionRegistry registry) {
::protos::internal::upb_extension_locker_global.store(
&lock_func, std::memory_order_release);
const std::string payload = GenerateTestData();
TestModel parsed_model =
::protos::Parse<TestModel>(payload, registry).value();
const auto test_main = [&] { EXPECT_EQ("str", parsed_model.str1()); };
const auto test_theme = [&] {
ASSERT_TRUE(::protos::HasExtension(&parsed_model, theme));
auto ext = ::protos::GetExtension(&parsed_model, theme);
ASSERT_OK(ext);
EXPECT_EQ((*ext)->ext_name(), "theme");
};
const auto test_theme_extension = [&] {
auto ext =
::protos::GetExtension(&parsed_model, ThemeExtension::theme_extension);
ASSERT_OK(ext);
EXPECT_EQ((*ext)->ext_name(), "theme_extension");
};
const auto test_serialize = [&] {
::upb::Arena arena;
EXPECT_OK(::protos::Serialize(&parsed_model, arena));
};
std::thread t1(test_main);
std::thread t2(test_main);
std::thread t3(test_theme);
std::thread t4(test_theme);
std::thread t5(test_theme_extension);
std::thread t6(test_theme_extension);
std::thread t7(test_serialize);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
test_main();
test_theme();
test_theme_extension();
}
TEST(CppGeneratedCode, ConcurrentAccessDoesNotRaceBothLazy) {
::upb::Arena arena;
TestConcurrentExtensionAccess({{}, arena});
}
TEST(CppGeneratedCode, ConcurrentAccessDoesNotRaceOneLazyOneEager) {
::upb::Arena arena;
TestConcurrentExtensionAccess({{&theme}, arena});
TestConcurrentExtensionAccess({{&ThemeExtension::theme_extension}, arena});
}
TEST(CppGeneratedCode, ConcurrentAccessDoesNotRaceBothEager) {
::upb::Arena arena;
TestConcurrentExtensionAccess(
{{&theme, &ThemeExtension::theme_extension}, arena});
}
} // namespace
} // namespace protos_generator::test::protos

@ -0,0 +1,48 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_PROTOS_INTERNAL_H_
#define UPB_PROTOS_PROTOS_INTERNAL_H_
#include "upb/mem/arena.h"
#include "upb/message/message.h"
namespace protos::internal {
// Moves ownership of a message created in a source arena.
//
// Utility function to provide a way to move ownership across languages or VMs.
template <typename T>
T MoveMessage(upb_Message* msg, upb_Arena* arena) {
return T(msg, arena);
}
} // namespace protos::internal
#endif

@ -0,0 +1,60 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos/protos_internal.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "protos_generator/tests/test_model.upb.h"
#include "protos_generator/tests/test_model.upb.proto.h"
#include "upb/mem/arena.h"
namespace protos::testing {
namespace {
using ::protos_generator::test::protos::TestModel;
TEST(CppGeneratedCode, InternalMoveMessage) {
// Generate message (simulating message created in another VM/language)
upb_Arena* source_arena = upb_Arena_New();
protos_generator_test_TestModel* message =
protos_generator_test_TestModel_new(source_arena);
ASSERT_NE(message, nullptr);
protos_generator_test_TestModel_set_int_value_with_default(message, 123);
// Move ownership.
TestModel model =
protos::internal::MoveMessage<TestModel>(message, source_arena);
// Now that we have moved ownership, free original arena.
upb_Arena_Free(source_arena);
EXPECT_EQ(model.int_value_with_default(), 123);
}
} // namespace
} // namespace protos::testing

@ -0,0 +1,44 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_
#define THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_
#include <type_traits>
namespace protos::internal {
template <typename T, typename T2>
using add_const_if_T_is_const =
std::conditional_t<std::is_const_v<T>, const T2, T2>;
} // namespace protos::internal
#endif // THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_

@ -0,0 +1,309 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_REPEATED_FIELD_H_
#define UPB_PROTOS_REPEATED_FIELD_H_
#include <cstddef>
#include <iterator>
#include <type_traits>
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "protos/protos.h"
#include "protos/protos_traits.h"
#include "protos/repeated_field_iterator.h"
#include "upb/base/string_view.h"
#include "upb/collections/array.h"
#include "upb/collections/internal/array.h"
#include "upb/mem/arena.h"
#include "upb/message/copy.h"
// Must be last:
#include "upb/port/def.inc"
namespace protos {
namespace internal {
// Shared implementation of repeated fields for absl::string_view and
// message types for mutable and immutable variants.
//
// Immutable (const accessor), constructs this class with a nullptr upb_Array*
// when the underlying array in the message is empty.
//
// Mutable accessors on the other hand, will allocate a new empty non-null
// upb_Array* for the message when the RepeatedFieldProxy is constructed.
template <class T>
class RepeatedFieldProxyBase {
using Array = add_const_if_T_is_const<T, upb_Array>;
public:
explicit RepeatedFieldProxyBase(Array* arr, upb_Arena* arena)
: arr_(arr), arena_(arena) {}
size_t size() const { return arr_ != nullptr ? upb_Array_Size(arr_) : 0; }
bool empty() const { return size() == 0; }
protected:
// Returns upb_Array message member.
inline upb_Message* GetMessage(size_t n) const;
Array* arr_;
upb_Arena* arena_;
};
template <class T>
upb_Message* RepeatedFieldProxyBase<T>::GetMessage(size_t n) const {
auto** messages =
static_cast<upb_Message**>(upb_Array_MutableDataPtr(this->arr_));
return messages[n];
}
template <class T>
class RepeatedFieldProxyMutableBase : public RepeatedFieldProxyBase<T> {
public:
RepeatedFieldProxyMutableBase(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyBase<T>(arr, arena) {}
void clear() { upb_Array_Resize(this->arr_, 0, this->arena_); }
};
// RepeatedField proxy for repeated messages.
template <class T>
class RepeatedFieldProxy
: public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
RepeatedFieldProxyMutableBase<T>> {
static_assert(!std::is_same_v<T, absl::string_view>, "");
static_assert(!std::is_same_v<T, const absl::string_view>, "");
static_assert(!std::is_arithmetic_v<T>, "");
static constexpr bool kIsConst = std::is_const_v<T>;
public:
explicit RepeatedFieldProxy(const upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyBase<T>(arr, arena) {}
RepeatedFieldProxy(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyMutableBase<T>(arr, arena) {}
// Constructor used by ::protos::Ptr.
RepeatedFieldProxy(const RepeatedFieldProxy&) = default;
// T::CProxy [] operator specialization.
typename T::CProxy operator[](size_t n) const {
upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
return ::protos::internal::CreateMessage<typename std::remove_const_t<T>>(
(upb_Message*)message_value.msg_val, this->arena_);
}
// TODO(b:/280069986) : Audit/Finalize based on Iterator Design.
// T::Proxy [] operator specialization.
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
typename T::Proxy operator[](size_t n) {
return ::protos::internal::CreateMessageProxy<T>(this->GetMessage(n),
this->arena_);
}
// Mutable message reference specialization.
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(const T& t) {
upb_MessageValue message_value;
message_value.msg_val = upb_Message_DeepClone(
PrivateAccess::GetInternalMsg(&t), ::protos::internal::GetMiniTable(&t),
this->arena_);
upb_Array_Append(this->arr_, message_value, this->arena_);
}
// Mutable message add using move.
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(T&& msg) {
upb_MessageValue message_value;
message_value.msg_val = PrivateAccess::GetInternalMsg(&msg);
upb_Arena_Fuse(GetArena(&msg), this->arena_);
upb_Array_Append(this->arr_, message_value, this->arena_);
T moved_msg = std::move(msg);
}
private:
friend class ::protos::Ptr<T>;
};
// RepeatedField proxy for repeated strings.
template <class T>
class RepeatedFieldStringProxy
: public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
RepeatedFieldProxyMutableBase<T>> {
static_assert(std::is_same_v<T, absl::string_view> ||
std::is_same_v<T, const absl::string_view>,
"");
static constexpr bool kIsConst = std::is_const_v<T>;
public:
using value_type = std::remove_const_t<T>;
using size_type = size_t;
using difference_type = ptrdiff_t;
using iterator = internal::Iterator<StringIteratorPolicy<T>>;
using reference = typename iterator::reference;
using pointer = typename iterator::pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
// Immutable constructor.
explicit RepeatedFieldStringProxy(const upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyBase<T>(arr, arena) {}
// Mutable constructor.
RepeatedFieldStringProxy(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyMutableBase<T>(arr, arena) {}
// Constructor used by ::protos::Ptr.
RepeatedFieldStringProxy(const RepeatedFieldStringProxy&) = default;
reference operator[](size_t n) const { return begin()[n]; }
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(T t) {
upb_MessageValue message_value;
// Copy string to arena.
UPB_ASSERT(this->arena_);
char* data = (char*)upb_Arena_Malloc(this->arena_, t.size());
UPB_ASSERT(data);
memcpy(data, t.data(), t.size());
message_value.str_val = upb_StringView_FromDataAndSize(data, t.size());
upb_Array_Append(this->arr_, message_value, this->arena_);
}
iterator begin() const { return iterator({this->arr_, this->arena_, 0}); }
iterator end() const {
return iterator({this->arr_, this->arena_, this->size()});
}
reverse_iterator rbegin() const { return reverse_iterator(end()); }
reverse_iterator rend() const { return reverse_iterator(begin()); }
};
// RepeatedField proxy for repeated scalar types.
template <typename T>
class RepeatedFieldScalarProxy
: public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
RepeatedFieldProxyMutableBase<T>> {
static_assert(std::is_arithmetic_v<T>, "");
static constexpr bool kIsConst = std::is_const_v<T>;
public:
using value_type = std::remove_const_t<T>;
using size_type = size_t;
using difference_type = ptrdiff_t;
using iterator = internal::Iterator<ScalarIteratorPolicy<T>>;
using reference = typename iterator::reference;
using pointer = typename iterator::pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
explicit RepeatedFieldScalarProxy(const upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyBase<T>(arr, arena) {}
RepeatedFieldScalarProxy(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyMutableBase<T>(arr, arena) {}
// Constructor used by ::protos::Ptr.
RepeatedFieldScalarProxy(const RepeatedFieldScalarProxy&) = default;
T operator[](size_t n) const {
upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
typename std::remove_const_t<T> val;
memcpy(&val, &message_value, sizeof(T));
return val;
}
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(T t) {
upb_MessageValue message_value;
memcpy(&message_value, &t, sizeof(T));
upb_Array_Append(this->arr_, message_value, this->arena_);
}
iterator begin() const { return iterator({unsafe_array()}); }
iterator cbegin() const { return begin(); }
iterator end() const { return iterator({unsafe_array() + this->size()}); }
iterator cend() const { return end(); }
// Reverse iterator support.
reverse_iterator rbegin() const { return reverse_iterator(end()); }
reverse_iterator rend() const { return reverse_iterator(begin()); }
reverse_iterator crbegin() const { return reverse_iterator(end()); }
reverse_iterator crend() const { return reverse_iterator(begin()); }
private:
T* unsafe_array() const {
if (kIsConst) {
const void* unsafe_ptr = ::upb_Array_DataPtr(this->arr_);
return static_cast<T*>(const_cast<void*>(unsafe_ptr));
}
if (!kIsConst) {
void* unsafe_ptr =
::upb_Array_MutableDataPtr(const_cast<upb_Array*>(this->arr_));
return static_cast<T*>(unsafe_ptr);
}
}
};
} // namespace internal
template <typename T>
class RepeatedField {
static constexpr bool kIsString = std::is_same_v<T, absl::string_view>;
static constexpr bool kIsScalar = std::is_arithmetic_v<T>;
public:
using Proxy = std::conditional_t<
kIsScalar, internal::RepeatedFieldScalarProxy<T>,
std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
internal::RepeatedFieldProxy<T>>>;
using CProxy = std::conditional_t<
kIsScalar, internal::RepeatedFieldScalarProxy<const T>,
std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<const T>,
internal::RepeatedFieldProxy<const T>>>;
// TODO(b/286451125): T supports incomplete type from fwd.h forwarding headers
// We would like to reference T::CProxy. Validate forwarding header design.
using ValueProxy = std::conditional_t<
kIsScalar, T,
std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<T>>>;
using ValueCProxy = std::conditional_t<
kIsScalar, const T,
std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<const T>>>;
using Access = std::conditional_t<
kIsScalar, internal::RepeatedFieldScalarProxy<T>,
std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
internal::RepeatedFieldProxy<T>>>;
};
} // namespace protos
#include "upb/port/undef.inc"
#endif // UPB_PROTOS_REPEATED_FIELD_H_

@ -0,0 +1,356 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_
#define UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_
#include <cstddef>
#include <cstring>
#include <iterator>
#include <type_traits>
#include "absl/strings/string_view.h"
#include "protos/protos.h"
#include "upb/base/string_view.h"
#include "upb/collections/array.h"
#include "upb/mem/arena.h"
#include "upb/message/copy.h"
// Must be last:
#include "upb/port/def.inc"
namespace protos {
namespace internal {
// TODO(b/279086429): Implement std iterator for messages
template <typename T>
class RepeatedFieldScalarProxy;
template <typename T>
class RepeatedFieldStringProxy;
struct IteratorTestPeer;
template <typename T>
class Iterator;
template <typename PolicyT>
class ReferenceProxy;
template <typename PolicyT>
class InjectedRelationalsImpl {
using RP = ReferenceProxy<PolicyT>;
using V = typename PolicyT::value_type;
friend bool operator==(RP a, V b) { return static_cast<V>(a) == b; }
friend bool operator==(V a, RP b) { return a == static_cast<V>(b); }
friend bool operator==(RP a, RP b) {
return static_cast<V>(a) == static_cast<V>(b);
}
friend bool operator!=(RP a, V b) { return static_cast<V>(a) != b; }
friend bool operator!=(V a, RP b) { return a != static_cast<V>(b); }
friend bool operator!=(RP a, RP b) {
return static_cast<V>(a) != static_cast<V>(b);
}
friend bool operator<(RP a, V b) { return static_cast<V>(a) < b; }
friend bool operator<(V a, RP b) { return a < static_cast<V>(b); }
friend bool operator<(RP a, RP b) {
return static_cast<V>(a) < static_cast<V>(b);
}
friend bool operator<=(RP a, V b) { return static_cast<V>(a) <= b; }
friend bool operator<=(V a, RP b) { return a <= static_cast<V>(b); }
friend bool operator<=(RP a, RP b) {
return static_cast<V>(a) <= static_cast<V>(b);
}
friend bool operator>(RP a, V b) { return static_cast<V>(a) > b; }
friend bool operator>(V a, RP b) { return a > static_cast<V>(b); }
friend bool operator>(RP a, RP b) {
return static_cast<V>(a) > static_cast<V>(b);
}
friend bool operator>=(RP a, V b) { return static_cast<V>(a) >= b; }
friend bool operator>=(V a, RP b) { return a >= static_cast<V>(b); }
friend bool operator>=(RP a, RP b) {
return static_cast<V>(a) >= static_cast<V>(b);
}
};
class NoInjectedRelationalsImpl {};
// We need to inject relationals for the string references because the
// relationals for string_view are templates and won't allow for implicit
// conversions from ReferenceProxy to string_view before deduction.
template <typename PolicyT>
using InjectedRelationals = std::conditional_t<
std::is_same_v<std::remove_const_t<typename PolicyT::value_type>,
absl::string_view>,
InjectedRelationalsImpl<PolicyT>, NoInjectedRelationalsImpl>;
template <typename PolicyT>
class ReferenceProxy : InjectedRelationals<PolicyT> {
using value_type = typename PolicyT::value_type;
public:
ReferenceProxy(const ReferenceProxy&) = default;
ReferenceProxy& operator=(const ReferenceProxy& other) {
// Assign through the references
// TODO(sbenza): Make this better for strings to avoid the copy.
it_.Set(other.it_.Get());
return *this;
}
friend void swap(ReferenceProxy a, ReferenceProxy b) { a.it_.swap(b.it_); }
operator value_type() const { return it_.Get(); }
void operator=(const value_type& value) const { it_.Set(value); }
void operator=(value_type&& value) const { it_.Set(std::move(value)); }
Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); }
private:
friend IteratorTestPeer;
friend ReferenceProxy<typename PolicyT::AddConst>;
friend Iterator<PolicyT>;
explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {}
typename PolicyT::Payload it_;
};
template <template <typename> class PolicyTemplate, typename T>
class ReferenceProxy<PolicyTemplate<const T>>
: InjectedRelationals<PolicyTemplate<const T>> {
using PolicyT = PolicyTemplate<const T>;
using value_type = typename PolicyT::value_type;
public:
ReferenceProxy(ReferenceProxy<PolicyTemplate<T>> p) : it_(p.it_) {}
ReferenceProxy(const ReferenceProxy&) = default;
ReferenceProxy& operator=(const ReferenceProxy&) = delete;
operator value_type() const { return it_.Get(); }
Iterator<PolicyT> operator&() const { return Iterator<PolicyT>(it_); }
private:
friend IteratorTestPeer;
friend Iterator<PolicyT>;
explicit ReferenceProxy(typename PolicyT::Payload elem) : it_(elem) {}
typename PolicyT::Payload it_;
};
template <typename PolicyT>
class Iterator {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = std::remove_const_t<typename PolicyT::value_type>;
using difference_type = std::ptrdiff_t;
using pointer = Iterator;
using reference = ReferenceProxy<PolicyT>;
constexpr Iterator() noexcept : it_(nullptr) {}
Iterator(const Iterator& other) = default;
Iterator& operator=(const Iterator& other) = default;
template <
typename P = PolicyT,
typename = std::enable_if_t<std::is_const<typename P::value_type>::value>>
Iterator(const Iterator<typename P::RemoveConst>& other) : it_(other.it_) {}
constexpr reference operator*() const noexcept { return reference(it_); }
// No operator-> needed because T is a scalar.
private:
// Hide the internal type.
using iterator = Iterator;
public:
// {inc,dec}rementable
constexpr iterator& operator++() noexcept {
it_.AddOffset(1);
return *this;
}
constexpr iterator operator++(int) noexcept {
auto copy = *this;
++*this;
return copy;
}
constexpr iterator& operator--() noexcept {
it_.AddOffset(-1);
return *this;
}
constexpr iterator operator--(int) noexcept {
auto copy = *this;
--*this;
return copy;
}
// equality_comparable
friend constexpr bool operator==(const iterator& x,
const iterator& y) noexcept {
return x.it_.Index() == y.it_.Index();
}
friend constexpr bool operator!=(const iterator& x,
const iterator& y) noexcept {
return !(x == y);
}
// less_than_comparable
friend constexpr bool operator<(const iterator& x,
const iterator& y) noexcept {
return x.it_.Index() < y.it_.Index();
}
friend constexpr bool operator<=(const iterator& x,
const iterator& y) noexcept {
return !(y < x);
}
friend constexpr bool operator>(const iterator& x,
const iterator& y) noexcept {
return y < x;
}
friend constexpr bool operator>=(const iterator& x,
const iterator& y) noexcept {
return !(x < y);
}
constexpr iterator& operator+=(difference_type d) noexcept {
it_.AddOffset(d);
return *this;
}
constexpr iterator operator+(difference_type d) const noexcept {
auto copy = *this;
copy += d;
return copy;
}
friend constexpr iterator operator+(const difference_type d,
iterator it) noexcept {
return it + d;
}
constexpr iterator& operator-=(difference_type d) noexcept {
it_.AddOffset(-d);
return *this;
}
constexpr iterator operator-(difference_type d) const noexcept {
auto copy = *this;
copy -= d;
return copy;
}
// indexable
constexpr reference operator[](difference_type d) const noexcept {
auto copy = *this;
copy += d;
return *copy;
}
// random access iterator
friend constexpr difference_type operator-(iterator x, iterator y) noexcept {
return x.it_.Index() - y.it_.Index();
}
private:
friend IteratorTestPeer;
friend ReferenceProxy<PolicyT>;
friend Iterator<typename PolicyT::AddConst>;
template <typename U>
friend class RepeatedFieldScalarProxy;
template <typename U>
friend class RepeatedFieldStringProxy;
// Create from internal::RepeatedFieldScalarProxy.
explicit Iterator(typename PolicyT::Payload it) noexcept : it_(it) {}
// The internal iterator.
typename PolicyT::Payload it_;
};
template <typename T>
struct ScalarIteratorPolicy {
using value_type = T;
using RemoveConst = ScalarIteratorPolicy<std::remove_const_t<T>>;
using AddConst = ScalarIteratorPolicy<const T>;
struct Payload {
T* value;
void AddOffset(ptrdiff_t offset) { value += offset; }
T Get() const { return *value; }
void Set(T new_value) const { *value = new_value; }
T* Index() const { return value; }
void swap(Payload& other) {
using std::swap;
swap(*value, *other.value);
}
operator typename ScalarIteratorPolicy<const T>::Payload() const {
return {value};
}
};
};
template <typename T>
struct StringIteratorPolicy {
using value_type = T;
using RemoveConst = StringIteratorPolicy<std::remove_const_t<T>>;
using AddConst = StringIteratorPolicy<const T>;
struct Payload {
using Array =
std::conditional_t<std::is_const_v<T>, const upb_Array, upb_Array>;
Array* arr;
upb_Arena* arena;
size_t index;
void AddOffset(ptrdiff_t offset) { index += offset; }
absl::string_view Get() const {
upb_MessageValue message_value = upb_Array_Get(arr, index);
return absl::string_view(message_value.str_val.data,
message_value.str_val.size);
}
void Set(absl::string_view new_value) const {
char* data =
static_cast<char*>(upb_Arena_Malloc(arena, new_value.size()));
memcpy(data, new_value.data(), new_value.size());
upb_MessageValue message_value;
message_value.str_val =
upb_StringView_FromDataAndSize(data, new_value.size());
upb_Array_Set(arr, index, message_value);
}
size_t Index() const { return index; }
void swap(Payload& other) {
upb_MessageValue a = upb_Array_Get(this->arr, this->index);
upb_MessageValue b = upb_Array_Get(other.arr, other.index);
upb_Array_Set(this->arr, this->index, b);
upb_Array_Set(other.arr, other.index, a);
}
operator typename StringIteratorPolicy<const T>::Payload() const {
return {arr, arena, index};
}
};
};
} // namespace internal
} // namespace protos
#endif // UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_

@ -0,0 +1,478 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos/repeated_field_iterator.h"
#include <algorithm>
#include <array>
#include <numeric>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::ElementsAre;
namespace protos {
namespace internal {
template <typename T>
using ScalarRef = ReferenceProxy<ScalarIteratorPolicy<T>>;
template <typename T>
using ScalarIterator = Iterator<ScalarIteratorPolicy<T>>;
template <typename T>
using StringRef = ReferenceProxy<StringIteratorPolicy<T>>;
template <typename T>
using StringIterator = Iterator<StringIteratorPolicy<T>>;
struct IteratorTestPeer {
template <typename T>
static ScalarRef<T> MakeScalarRefProxy(T& ref) {
return ScalarRef<T>({&ref});
}
template <typename T>
static ScalarIterator<T> MakeScalarIterator(T* ptr) {
return ScalarIterator<T>({ptr});
}
template <typename T>
static StringRef<T> MakeStringRefProxy(upb_Array* arr, protos::Arena& arena) {
return StringRef<T>({arr, arena.ptr(), 0});
}
template <typename T>
static StringIterator<T> MakeStringIterator(upb_Array* arr,
protos::Arena& arena) {
return StringIterator<T>({arr, arena.ptr()});
}
};
namespace {
TEST(ScalarReferenceTest, BasicOperationsWork) {
int i = 0;
ScalarRef<int> p = IteratorTestPeer::MakeScalarRefProxy(i);
ScalarRef<const int> cp =
IteratorTestPeer::MakeScalarRefProxy(std::as_const(i));
EXPECT_EQ(i, 0);
p = 17;
EXPECT_EQ(i, 17);
EXPECT_EQ(p, 17);
EXPECT_EQ(cp, 17);
i = 13;
EXPECT_EQ(p, 13);
EXPECT_EQ(cp, 13);
EXPECT_FALSE((std::is_assignable<decltype(cp), int>::value));
// Check that implicit conversion works T -> const T
ScalarRef<const int> cp2 = p;
EXPECT_EQ(cp2, 13);
EXPECT_FALSE((std::is_convertible<decltype(cp), ScalarRef<int>>::value));
}
TEST(ScalarReferenceTest, AssignmentAndSwap) {
int i = 3;
int j = 5;
ScalarRef<int> p = IteratorTestPeer::MakeScalarRefProxy(i);
ScalarRef<int> p2 = IteratorTestPeer::MakeScalarRefProxy(j);
EXPECT_EQ(p, 3);
EXPECT_EQ(p2, 5);
swap(p, p2);
EXPECT_EQ(p, 5);
EXPECT_EQ(p2, 3);
p = p2;
EXPECT_EQ(p, 3);
EXPECT_EQ(p2, 3);
}
template <typename T, typename U>
std::array<bool, 6> RunCompares(const T& a, const U& b) {
// Verify some basic properties here.
// Equivalencies
EXPECT_EQ((a == b), (b == a));
EXPECT_EQ((a != b), (b != a));
EXPECT_EQ((a < b), (b > a));
EXPECT_EQ((a > b), (b < a));
EXPECT_EQ((a <= b), (b >= a));
EXPECT_EQ((a >= b), (b <= a));
// Opposites
EXPECT_NE((a == b), (a != b));
EXPECT_NE((a < b), (a >= b));
EXPECT_NE((a > b), (a <= b));
return {{
(a == b),
(a != b),
(a < b),
(a <= b),
(a > b),
(a >= b),
}};
}
template <typename T>
void TestScalarIterator(T* array) {
ScalarIterator<T> it = IteratorTestPeer::MakeScalarIterator(array);
// Copy
auto it2 = it;
EXPECT_THAT(RunCompares(it, it2),
ElementsAre(true, false, false, true, false, true));
// Increment
EXPECT_EQ(*++it, 11);
EXPECT_EQ(*it2, 10);
EXPECT_EQ(*it++, 11);
EXPECT_EQ(*it2, 10);
EXPECT_EQ(*it, 12);
EXPECT_EQ(*it2, 10);
EXPECT_THAT(RunCompares(it, it2),
ElementsAre(false, true, false, false, true, true));
// Assign
it2 = it;
EXPECT_EQ(*it, 12);
EXPECT_EQ(*it2, 12);
// Decrement
EXPECT_EQ(*--it, 11);
EXPECT_EQ(*it--, 11);
EXPECT_EQ(*it, 10);
it += 5;
EXPECT_EQ(*it, 15);
EXPECT_EQ(it - it2, 3);
EXPECT_EQ(it2 - it, -3);
it -= 3;
EXPECT_EQ(*it, 12);
EXPECT_EQ(it[6], 18);
EXPECT_EQ(it[-1], 11);
}
TEST(ScalarIteratorTest, BasicOperationsWork) {
int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
TestScalarIterator<const int>(array);
TestScalarIterator<int>(array);
}
TEST(ScalarIteratorTest, Convertibility) {
int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
ScalarIterator<int> it = IteratorTestPeer::MakeScalarIterator(array);
it += 4;
ScalarIterator<const int> cit = it;
EXPECT_EQ(*it, 14);
EXPECT_EQ(*cit, 14);
it += 2;
EXPECT_EQ(*it, 16);
EXPECT_EQ(*cit, 14);
cit = it;
EXPECT_EQ(*it, 16);
EXPECT_EQ(*cit, 16);
EXPECT_FALSE((std::is_convertible<ScalarIterator<const int>,
ScalarIterator<int>>::value));
EXPECT_FALSE((std::is_assignable<ScalarIterator<int>,
ScalarIterator<const int>>::value));
}
TEST(ScalarIteratorTest, MutabilityOnlyWorksOnMutable) {
int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
ScalarIterator<int> it = IteratorTestPeer::MakeScalarIterator(array);
EXPECT_EQ(array[3], 13);
it[3] = 113;
EXPECT_EQ(array[3], 113);
ScalarIterator<const int> cit = it;
EXPECT_FALSE((std::is_assignable<decltype(*cit), int>::value));
EXPECT_FALSE((std::is_assignable<decltype(cit[1]), int>::value));
}
TEST(ScalarIteratorTest, IteratorReferenceInteraction) {
int array[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
ScalarIterator<int> it = IteratorTestPeer::MakeScalarIterator(array);
EXPECT_EQ(it[4], 14);
// op& from references goes back to iterator.
ScalarIterator<int> it2 = &it[4];
EXPECT_EQ(it + 4, it2);
}
TEST(ScalarIteratorTest, IteratorBasedAlgorithmsWork) {
// We use a vector here to make testing it easier.
std::vector<int> v(10, 0);
ScalarIterator<int> it = IteratorTestPeer::MakeScalarIterator(v.data());
EXPECT_THAT(v, ElementsAre(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
std::iota(it, it + 10, 10);
EXPECT_THAT(v, ElementsAre(10, 11, 12, 13, 14, 15, 16, 17, 18, 19));
EXPECT_EQ(it + 5, std::find(it, it + 10, 15));
EXPECT_EQ(145, std::accumulate(it, it + 10, 0));
std::sort(it, it + 10, [](int a, int b) {
return std::tuple(a % 2, a) < std::tuple(b % 2, b);
});
EXPECT_THAT(v, ElementsAre(10, 12, 14, 16, 18, 11, 13, 15, 17, 19));
}
const char* CloneString(protos::Arena& arena, absl::string_view str) {
char* data = (char*)upb_Arena_Malloc(arena.ptr(), str.size());
memcpy(data, str.data(), str.size());
return data;
}
upb_Array* MakeStringArray(protos::Arena& arena,
const std::vector<std::string>& input) {
upb_Array* arr = upb_Array_New(arena.ptr(), kUpb_CType_String);
for (absl::string_view str : input) {
upb_MessageValue message_value;
message_value.str_val =
upb_StringView_FromDataAndSize(CloneString(arena, str), str.size());
upb_Array_Append(arr, message_value, arena.ptr());
}
return arr;
}
TEST(StringReferenceTest, BasicOperationsWork) {
protos::Arena arena;
upb_Array* arr = MakeStringArray(arena, {""});
auto read = [&] {
upb_MessageValue message_value = upb_Array_Get(arr, 0);
return absl::string_view(message_value.str_val.data,
message_value.str_val.size);
};
StringRef<absl::string_view> p =
IteratorTestPeer::MakeStringRefProxy<absl::string_view>(arr, arena);
StringRef<const absl::string_view> cp =
IteratorTestPeer::MakeStringRefProxy<const absl::string_view>(arr, arena);
EXPECT_EQ(read(), "");
EXPECT_EQ(p, "");
p = "ABC";
EXPECT_EQ(read(), "ABC");
EXPECT_EQ(p, "ABC");
EXPECT_EQ(cp, "ABC");
const_cast<char*>(read().data())[0] = 'X';
EXPECT_EQ(read(), "XBC");
EXPECT_EQ(p, "XBC");
EXPECT_EQ(cp, "XBC");
EXPECT_FALSE((std::is_assignable<decltype(cp), int>::value));
// Check that implicit conversion works T -> const T
StringRef<const absl::string_view> cp2 = p;
EXPECT_EQ(cp2, "XBC");
EXPECT_FALSE(
(std::is_convertible<decltype(cp), StringRef<absl::string_view>>::value));
EXPECT_THAT(RunCompares(p, "XBC"),
ElementsAre(true, false, false, true, false, true));
EXPECT_THAT(RunCompares(p, "YBC"),
ElementsAre(false, true, true, true, false, false));
EXPECT_THAT(RunCompares(p, "RBC"),
ElementsAre(false, true, false, false, true, true));
EXPECT_THAT(RunCompares(p, "XB"),
ElementsAre(false, true, false, false, true, true));
EXPECT_THAT(RunCompares(p, "XBCD"),
ElementsAre(false, true, true, true, false, false));
}
TEST(StringReferenceTest, AssignmentAndSwap) {
protos::Arena arena;
upb_Array* arr1 = MakeStringArray(arena, {"ABC"});
upb_Array* arr2 = MakeStringArray(arena, {"DEF"});
auto p = IteratorTestPeer::MakeStringRefProxy<absl::string_view>(arr1, arena);
auto p2 =
IteratorTestPeer::MakeStringRefProxy<absl::string_view>(arr2, arena);
EXPECT_EQ(p, "ABC");
EXPECT_EQ(p2, "DEF");
swap(p, p2);
EXPECT_EQ(p, "DEF");
EXPECT_EQ(p2, "ABC");
p = p2;
EXPECT_EQ(p, "ABC");
EXPECT_EQ(p2, "ABC");
}
template <typename T>
void TestStringIterator(protos::Arena& arena, upb_Array* array) {
StringIterator<T> it = IteratorTestPeer::MakeStringIterator<T>(array, arena);
// Copy
auto it2 = it;
EXPECT_THAT(RunCompares(it, it2),
ElementsAre(true, false, false, true, false, true));
// Increment
EXPECT_EQ(*++it, "11");
EXPECT_EQ(*it2, "10");
EXPECT_EQ(*it++, "11");
EXPECT_EQ(*it2, "10");
EXPECT_EQ(*it, "12");
EXPECT_EQ(*it2, "10");
EXPECT_THAT(RunCompares(it, it2),
ElementsAre(false, true, false, false, true, true));
// Assign
it2 = it;
EXPECT_EQ(*it, "12");
EXPECT_EQ(*it2, "12");
// Decrement
EXPECT_EQ(*--it, "11");
EXPECT_EQ(*it--, "11");
EXPECT_EQ(*it, "10");
it += 5;
EXPECT_EQ(*it, "15");
EXPECT_EQ(it - it2, 3);
EXPECT_EQ(it2 - it, -3);
it -= 3;
EXPECT_EQ(*it, "12");
EXPECT_EQ(it[6], "18");
EXPECT_EQ(it[-1], "11");
}
TEST(StringIteratorTest, BasicOperationsWork) {
protos::Arena arena;
auto* array = MakeStringArray(
arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"});
TestStringIterator<const absl::string_view>(arena, array);
TestStringIterator<absl::string_view>(arena, array);
}
TEST(StringIteratorTest, Convertibility) {
protos::Arena arena;
auto* array = MakeStringArray(
arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"});
StringIterator<absl::string_view> it =
IteratorTestPeer::MakeStringIterator<absl::string_view>(array, arena);
it += 4;
StringIterator<const absl::string_view> cit = it;
EXPECT_EQ(*it, "14");
EXPECT_EQ(*cit, "14");
it += 2;
EXPECT_EQ(*it, "16");
EXPECT_EQ(*cit, "14");
cit = it;
EXPECT_EQ(*it, "16");
EXPECT_EQ(*cit, "16");
EXPECT_FALSE((std::is_convertible<StringIterator<const absl::string_view>,
StringIterator<absl::string_view>>::value));
EXPECT_FALSE(
(std::is_assignable<StringIterator<absl::string_view>,
StringIterator<const absl::string_view>>::value));
}
TEST(StringIteratorTest, MutabilityOnlyWorksOnMutable) {
protos::Arena arena;
auto* array = MakeStringArray(
arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"});
StringIterator<absl::string_view> it =
IteratorTestPeer::MakeStringIterator<absl::string_view>(array, arena);
auto read = [&] {
upb_MessageValue message_value = upb_Array_Get(array, 3);
return absl::string_view(message_value.str_val.data,
message_value.str_val.size);
};
EXPECT_EQ(read(), "13");
it[3] = "113";
EXPECT_EQ(read(), "113");
StringIterator<const absl::string_view> cit = it;
EXPECT_FALSE((std::is_assignable<decltype(*cit), absl::string_view>::value));
EXPECT_FALSE(
(std::is_assignable<decltype(cit[1]), absl::string_view>::value));
}
TEST(StringIteratorTest, IteratorReferenceInteraction) {
protos::Arena arena;
auto* array = MakeStringArray(
arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"});
StringIterator<absl::string_view> it =
IteratorTestPeer::MakeStringIterator<absl::string_view>(array, arena);
EXPECT_EQ(it[4], "14");
// op& from references goes back to iterator.
StringIterator<absl::string_view> it2 = &it[4];
EXPECT_EQ(it + 4, it2);
}
TEST(StringIteratorTest, IteratorBasedAlgorithmsWork) {
protos::Arena arena;
auto* array = MakeStringArray(
arena, {"10", "11", "12", "13", "14", "15", "16", "17", "18", "19"});
StringIterator<absl::string_view> it =
IteratorTestPeer::MakeStringIterator<absl::string_view>(array, arena);
auto read = [&] {
std::vector<absl::string_view> v;
for (int i = 0; i < 10; ++i) {
upb_MessageValue message_value = upb_Array_Get(array, i);
v.emplace_back(message_value.str_val.data, message_value.str_val.size);
}
return v;
};
EXPECT_THAT(read(), ElementsAre("10", "11", "12", "13", "14", //
"15", "16", "17", "18", "19"));
std::sort(it, it + 10, [](absl::string_view a, absl::string_view b) {
return std::tuple(a[1] % 2, a) < std::tuple(b[1] % 2, b);
});
EXPECT_THAT(read(), ElementsAre("10", "12", "14", "16", "18", //
"11", "13", "15", "17", "19"));
// Now sort with the default less.
std::sort(it, it + 10);
EXPECT_THAT(read(), ElementsAre("10", "11", "12", "13", "14", //
"15", "16", "17", "18", "19"));
// Mutable algorithm
std::generate(it, it + 10,
[i = 0]() mutable { return std::string(i++, 'x'); });
EXPECT_THAT(read(),
ElementsAre("", "x", "xx", "xxx", "xxxx", "xxxxx", "xxxxxx",
"xxxxxxx", "xxxxxxxx", "xxxxxxxxx"));
}
} // namespace
} // namespace internal
} // namespace protos

@ -0,0 +1,119 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load(
"//bazel:build_defs.bzl",
"UPB_DEFAULT_CPPOPTS",
)
# begin:google_only
# package(default_applicable_licenses = ["//:license"])
# end:google_only
licenses(["notice"])
cc_binary(
name = "protoc-gen-upb-protos",
srcs = [
"protoc-gen-upb-protos.cc",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":gen_utils",
":generator",
":names",
":output",
"//upbc:file_layout",
"@com_google_protobuf//:protobuf",
"@com_google_protobuf//src/google/protobuf/compiler:code_generator",
],
)
cc_library(
name = "generator",
srcs = [
"gen_accessors.cc",
"gen_enums.cc",
"gen_extensions.cc",
"gen_messages.cc",
"gen_repeated_fields.cc",
],
hdrs = [
"gen_accessors.h",
"gen_enums.h",
"gen_extensions.h",
"gen_messages.h",
"gen_repeated_fields.h",
],
visibility = ["//visibility:private"],
deps = [
":gen_utils",
":names",
":output",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/strings",
"//upbc:common",
"//upbc:file_layout",
"//upbc:keywords",
"//upbc:names",
"@com_google_protobuf//:protobuf",
],
)
cc_library(
name = "output",
srcs = ["output.cc"],
hdrs = ["output.h"],
visibility = ["//visibility:private"],
deps = [
"@com_google_absl//absl/log:absl_log",
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
],
)
cc_library(
name = "gen_utils",
srcs = ["gen_utils.cc"],
hdrs = ["gen_utils.h"],
visibility = ["//visibility:public"],
deps = [
"@com_google_absl//absl/strings",
"@com_google_protobuf//:protobuf",
"@com_google_protobuf//src/google/protobuf/compiler:code_generator",
],
)
cc_library(
name = "names",
srcs = ["names.cc"],
hdrs = ["names.h"],
visibility = ["//visibility:private"],
deps = [
":output",
"//upbc:keywords",
],
)

@ -0,0 +1,584 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/gen_accessors.h"
#include <string>
#include "absl/container/flat_hash_set.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor.h"
#include "protos_generator/gen_repeated_fields.h"
#include "protos_generator/gen_utils.h"
#include "protos_generator/names.h"
#include "protos_generator/output.h"
#include "upbc/common.h"
#include "upbc/keywords.h"
#include "upbc/names.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
using NameToFieldDescriptorMap =
absl::flat_hash_map<absl::string_view, const protobuf::FieldDescriptor*>;
void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view resolved_upbc_name,
Output& output);
void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view resolved_upbc_name,
Output& output);
void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view resolved_upbc_name,
Output& output);
void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view class_name, Output& output);
// Returns C++ class member name by resolving naming conflicts across
// proto field names (such as clear_ prefixes) and keyword collisions.
//
// The Upb C generator prefixes all accessors with package and class names
// avoiding collisions. Therefore we need to use raw field names when calling
// into C accessors but need to fully resolve conflicts for C++ class members.
std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
const NameToFieldDescriptorMap& field_names);
NameToFieldDescriptorMap CreateFieldNameMap(
const protobuf::Descriptor* message) {
NameToFieldDescriptorMap field_names;
for (int i = 0; i < message->field_count(); i++) {
const protobuf::FieldDescriptor* field = message->field(i);
field_names.emplace(field->name(), field);
}
return field_names;
}
void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
Output& output) {
// Generate const methods.
OutputIndenter i(output);
auto field_names = CreateFieldNameMap(desc);
auto upbc_field_names = upbc::CreateFieldNameMap(desc);
for (const auto* field : FieldNumberOrder(desc)) {
std::string resolved_field_name = ResolveFieldName(field, field_names);
std::string resolved_upbc_name =
upbc::ResolveFieldName(field, upbc_field_names);
WriteFieldAccessorHazzer(desc, field, resolved_field_name,
resolved_upbc_name, output);
WriteFieldAccessorClear(desc, field, resolved_field_name,
resolved_upbc_name, output);
if (field->is_map()) {
WriteMapFieldAccessors(desc, field, resolved_field_name,
resolved_upbc_name, output);
} else if (desc->options().map_entry()) {
// TODO(b/237399867) Implement map entry
} else if (field->is_repeated()) {
WriteRepeatedFieldsInMessageHeader(desc, field, resolved_field_name,
resolved_upbc_name, output);
} else {
// non-repeated.
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
output(R"cc(
$0 $1() const;
void set_$1($0 value);
)cc",
CppConstType(field), resolved_field_name);
} else if (field->cpp_type() ==
protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
output(R"cc(
$1 $2() const;
$0 mutable_$2();
)cc",
MessagePtrConstType(field, /* const */ false),
MessagePtrConstType(field, /* const */ true),
resolved_field_name, resolved_upbc_name);
} else {
output(
R"cc(
inline $0 $1() const { return $2_$3(msg_); }
inline void set_$1($0 value) { return $2_set_$3(msg_, value); }
)cc",
CppConstType(field), resolved_field_name, MessageName(desc),
resolved_upbc_name);
}
}
}
}
void WriteFieldAccessorHazzer(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
const absl::string_view resolved_field_name,
const absl::string_view resolved_upbc_name,
Output& output) {
// Generate hazzer (if any).
if (field->has_presence()) {
// Has presence.
output("inline bool has_$0() const { return $1_has_$2(msg_); }\n",
resolved_field_name, MessageName(desc), resolved_upbc_name);
}
}
void WriteFieldAccessorClear(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
const absl::string_view resolved_field_name,
const absl::string_view resolved_upbc_name,
Output& output) {
if (field->has_presence()) {
output("void clear_$0() { $2_clear_$1(msg_); }\n", resolved_field_name,
resolved_upbc_name, MessageName(desc));
}
}
void WriteMapFieldAccessors(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
const absl::string_view resolved_field_name,
const absl::string_view resolved_upbc_name,
Output& output) {
const protobuf::Descriptor* entry = field->message_type();
const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
output(
R"cc(
inline size_t $0_size() const { return $1_$3_size(msg_); }
inline void clear_$0() { $1_clear_$3(msg_); }
void delete_$0($2 key);
)cc",
resolved_field_name, MessageName(desc), CppConstType(key),
resolved_upbc_name);
if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
output(
R"cc(
bool set_$0($1 key, $3 value);
bool set_$0($1 key, $4 value);
absl::StatusOr<$3> get_$0($1 key);
)cc",
resolved_field_name, CppConstType(key), CppConstType(val),
MessagePtrConstType(val, /* is_const */ true),
MessagePtrConstType(val, /* is_const */ false));
} else {
output(
R"cc(
bool set_$0($1 key, $2 value);
absl::StatusOr<$2> get_$0($1 key);
)cc",
resolved_field_name, CppConstType(key), CppConstType(val));
}
}
void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output) {
std::string class_name = ClassName(desc);
absl::StrAppend(&class_name, "Access");
output("namespace internal {\n");
const char arena_expression[] = "arena_";
auto field_names = CreateFieldNameMap(desc);
auto upbc_field_names = upbc::CreateFieldNameMap(desc);
// Generate const methods.
OutputIndenter i(output);
for (const auto* field : FieldNumberOrder(desc)) {
std::string resolved_field_name = ResolveFieldName(field, field_names);
std::string resolved_upbc_name =
upbc::ResolveFieldName(field, upbc_field_names);
if (field->is_map()) {
WriteMapAccessorDefinitions(desc, field, resolved_field_name, class_name,
output);
} else if (desc->options().map_entry()) {
// TODO(b/237399867) Implement map entry
} else if (field->is_repeated()) {
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
WriteRepeatedMessageAccessor(desc, field, resolved_field_name,
class_name, output);
} else if (field->cpp_type() ==
protobuf::FieldDescriptor::CPPTYPE_STRING) {
WriteRepeatedStringAccessor(desc, field, resolved_field_name,
class_name, output);
} else {
WriteRepeatedScalarAccessor(desc, field, resolved_field_name,
class_name, output);
}
} else {
// non-repeated field.
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
output(
R"cc(
$1 $0::$2() const {
return ::protos::UpbStrToStringView($3_$4(msg_));
}
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(desc), resolved_upbc_name);
// Set string.
output(
R"cc(
void $0::set_$2($1 value) {
$4_set_$3(msg_, ::protos::UpbStrFromStringView(value, $5));
}
)cc",
class_name, CppConstType(field), resolved_field_name,
resolved_upbc_name, MessageName(desc), arena_expression);
} else if (field->cpp_type() ==
protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
output(
R"cc(
$1 $0::$2() const {
if (!has_$2()) {
return $4::default_instance();
}
return ::protos::internal::CreateMessage<$4>(
(upb_Message*)($3_$5(msg_)), arena_);
}
)cc",
class_name, MessagePtrConstType(field, /* is_const */ true),
resolved_field_name, MessageName(desc),
MessageBaseType(field, /* maybe_const */ false),
resolved_upbc_name);
output(
R"cc(
$1 $0::mutable_$2() {
return ::protos::internal::CreateMessageProxy<$4>(
(upb_Message*)($3_mutable_$5(msg_, $6)), $6);
}
)cc",
class_name, MessagePtrConstType(field, /* is_const */ false),
resolved_field_name, MessageName(desc),
MessageBaseType(field, /* maybe_const */ false), resolved_upbc_name,
arena_expression);
}
}
}
output("\n");
output("} // namespace internal\n\n");
}
void WriteMapAccessorDefinitions(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
const absl::string_view resolved_field_name,
const absl::string_view class_name,
Output& output) {
const protobuf::Descriptor* entry = field->message_type();
const protobuf::FieldDescriptor* key = entry->FindFieldByNumber(1);
const protobuf::FieldDescriptor* val = entry->FindFieldByNumber(2);
absl::string_view upbc_name = field->name();
absl::string_view converted_key_name = "key";
absl::string_view optional_conversion_code = "";
if (key->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
// Insert conversion from absl::string_view to upb_StringView.
// Creates upb_StringView on stack to prevent allocation.
converted_key_name = "upb_key";
optional_conversion_code =
"upb_StringView upb_key = {key.data(), key.size()};\n";
}
if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
output(
R"cc(
bool $0::set_$1($2 key, $3 value) {
upb_Message* clone = upb_Message_DeepClone(
::protos::internal::PrivateAccess::GetInternalMsg(value), &$9,
arena_);
$6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
}
)cc",
class_name, resolved_field_name, CppConstType(key),
MessagePtrConstType(val, /* is_const */ true), MessageName(message),
MessageName(val->message_type()), optional_conversion_code,
converted_key_name, upbc_name,
::upbc::MessageInit(val->message_type()->full_name()));
output(
R"cc(
bool $0::set_$1($2 key, $3 value) {
upb_Message* clone = upb_Message_DeepClone(
::protos::internal::PrivateAccess::GetInternalMsg(value), &$9,
arena_);
$6return $4_$8_set(msg_, $7, ($5*)clone, arena_);
}
)cc",
class_name, resolved_field_name, CppConstType(key),
MessagePtrConstType(val, /* is_const */ false), MessageName(message),
MessageName(val->message_type()), optional_conversion_code,
converted_key_name, upbc_name,
::upbc::MessageInit(val->message_type()->full_name()));
output(
R"cc(
absl::StatusOr<$3> $0::get_$1($2 key) {
$5* msg_value;
$7bool success = $4_$9_get(msg_, $8, &msg_value);
if (success) {
return ::protos::internal::CreateMessage<$6>(msg_value, arena_);
}
return absl::NotFoundError("");
}
)cc",
class_name, resolved_field_name, CppConstType(key),
MessagePtrConstType(val, /* is_const */ true), MessageName(message),
MessageName(val->message_type()),
QualifiedClassName(val->message_type()), optional_conversion_code,
converted_key_name, upbc_name);
output(
R"cc(
void $0::delete_$1($2 key) { $6$4_$8_delete(msg_, $7); }
)cc",
class_name, resolved_field_name, CppConstType(key),
MessagePtrConstType(val, /* is_const */ false), MessageName(message),
MessageName(val->message_type()), optional_conversion_code,
converted_key_name, upbc_name);
} else if (val->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
output(
R"cc(
bool $0::set_$1($2 key, $3 value) {
$5return $4_$7_set(msg_, $6,
::protos::UpbStrFromStringView(value, arena_),
arena_);
}
)cc",
class_name, resolved_field_name, CppConstType(key), CppConstType(val),
MessageName(message), optional_conversion_code, converted_key_name,
upbc_name);
output(
R"cc(
absl::StatusOr<$3> $0::get_$1($2 key) {
upb_StringView value;
$5bool success = $4_$7_get(msg_, $6, &value);
if (success) {
return absl::string_view(value.data, value.size);
}
return absl::NotFoundError("");
}
)cc",
class_name, resolved_field_name, CppConstType(key), CppConstType(val),
MessageName(message), optional_conversion_code, converted_key_name,
upbc_name);
output(
R"cc(
void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
)cc",
class_name, resolved_field_name, CppConstType(key), CppConstType(val),
MessageName(message), optional_conversion_code, converted_key_name,
upbc_name);
} else {
output(
R"cc(
bool $0::set_$1($2 key, $3 value) {
$5return $4_$7_set(msg_, $6, value, arena_);
}
)cc",
class_name, resolved_field_name, CppConstType(key), CppConstType(val),
MessageName(message), optional_conversion_code, converted_key_name,
upbc_name);
output(
R"cc(
absl::StatusOr<$3> $0::get_$1($2 key) {
$3 value;
$5bool success = $4_$7_get(msg_, $6, &value);
if (success) {
return value;
}
return absl::NotFoundError("");
}
)cc",
class_name, resolved_field_name, CppConstType(key), CppConstType(val),
MessageName(message), optional_conversion_code, converted_key_name,
upbc_name);
output(
R"cc(
void $0::delete_$1($2 key) { $5$4_$7_delete(msg_, $6); }
)cc",
class_name, resolved_field_name, CppConstType(key), CppConstType(val),
MessageName(message), optional_conversion_code, converted_key_name,
upbc_name);
}
}
void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
MessageClassType handle_type, Output& output) {
bool read_only = handle_type == MessageClassType::kMessageCProxy;
// Generate const methods.
OutputIndenter i(output);
std::string class_name = ClassName(desc);
auto field_names = CreateFieldNameMap(desc);
for (const auto* field : FieldNumberOrder(desc)) {
std::string resolved_field_name = ResolveFieldName(field, field_names);
// Generate hazzer (if any).
if (field->has_presence()) {
output("using $0Access::has_$1;\n", class_name, resolved_field_name);
output("using $0Access::clear_$1;\n", class_name, resolved_field_name);
}
if (field->is_map()) {
output(
R"cc(
using $0Access::$1_size;
using $0Access::clear_$1;
using $0Access::delete_$1;
using $0Access::get_$1;
using $0Access::set_$1;
)cc",
class_name, resolved_field_name);
} else if (desc->options().map_entry()) {
// TODO(b/237399867) Implement map entry
} else if (field->is_repeated()) {
WriteRepeatedFieldUsingAccessors(field, class_name, resolved_field_name,
output, read_only);
} else {
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
output("using $0Access::$1;\n", ClassName(desc), resolved_field_name);
if (!read_only) {
output("using $0Access::mutable_$1;\n", class_name,
resolved_field_name);
}
} else {
output("using $0Access::$1;\n", class_name, resolved_field_name);
if (!read_only) {
output("using $0Access::set_$1;\n", class_name, resolved_field_name);
}
}
}
}
for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
output("using $0Access::$1_case;\n", class_name, oneof->name());
output("using $0Access::$1Case;\n", class_name,
ToCamelCase(oneof->name(), /*lower_first=*/false));
for (int j = 0; j < oneof->field_count(); ++j) {
const protobuf::FieldDescriptor* field = oneof->field(j);
output("using $0Access::k$1;\n", class_name,
ToCamelCase(field->name(), /*lower_first=*/false),
field->number());
}
output("using $0Access::$1_NOT_SET;\n", class_name,
absl::AsciiStrToUpper(oneof->name()));
}
}
void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc,
Output& output) {
// Generate const methods.
OutputIndenter i(output);
std::string class_name = ClassName(desc);
auto field_names = CreateFieldNameMap(desc);
for (int i = 0; i < desc->real_oneof_decl_count(); ++i) {
const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i);
output("enum $0Case {\n",
ToCamelCase(oneof->name(), /*lower_first=*/false));
for (int j = 0; j < oneof->field_count(); ++j) {
const protobuf::FieldDescriptor* field = oneof->field(j);
output(" k$0 = $1,\n", ToCamelCase(field->name(), /*lower_first=*/false),
field->number());
}
output(" $0_NOT_SET = 0,\n", absl::AsciiStrToUpper(oneof->name()));
output("};\n\n");
output("$0Case $1_case() const {\n",
ToCamelCase(oneof->name(), /*lower_first=*/false), oneof->name());
for (int j = 0; j < oneof->field_count(); ++j) {
const protobuf::FieldDescriptor* field = oneof->field(j);
std::string resolved_field_name = ResolveFieldName(field, field_names);
output(" if (has_$0()) { return k$1; }\n", resolved_field_name,
ToCamelCase(field->name(), /*lower_first=*/false));
}
output(" return $0_NOT_SET;\n", absl::AsciiStrToUpper(oneof->name()));
output("}\n;");
}
}
std::string ResolveFieldName(const protobuf::FieldDescriptor* field,
const NameToFieldDescriptorMap& field_names) {
// C++ implementation specific reserved names.
static const auto& kReservedNames =
*new absl::flat_hash_set<absl::string_view>({
"msg",
"msg_",
"arena",
"arena_",
});
// C++ specific prefixes used by code generator for field access.
static constexpr absl::string_view kClearMethodPrefix = "clear_";
static constexpr absl::string_view kSetMethodPrefix = "set_";
static constexpr absl::string_view kHasMethodPrefix = "has_";
static constexpr absl::string_view kDeleteMethodPrefix = "delete_";
static constexpr absl::string_view kAddToRepeatedMethodPrefix = "add_";
static constexpr absl::string_view kResizeArrayMethodPrefix = "resize_";
// List of generated accessor prefixes to check against.
// Example:
// optional repeated string phase = 236;
// optional bool clear_phase = 237;
static constexpr absl::string_view kAccessorPrefixes[] = {
kClearMethodPrefix, kDeleteMethodPrefix, kAddToRepeatedMethodPrefix,
kResizeArrayMethodPrefix, kSetMethodPrefix, kHasMethodPrefix};
absl::string_view field_name = field->name();
if (kReservedNames.count(field_name) > 0) {
if (absl::EndsWith(field_name, "_")) {
return absl::StrCat(field_name, "_");
} else {
return absl::StrCat(field_name, "__");
}
}
for (const auto prefix : kAccessorPrefixes) {
// If field name starts with a prefix such as clear_ and the proto
// contains a field name with trailing end, depending on type of field
// (repeated, map, message) we have a conflict to resolve.
if (absl::StartsWith(field_name, prefix)) {
auto match = field_names.find(field_name.substr(prefix.size()));
if (match != field_names.end()) {
const auto* candidate = match->second;
if (candidate->is_repeated() || candidate->is_map() ||
(candidate->cpp_type() ==
protobuf::FieldDescriptor::CPPTYPE_STRING &&
prefix == kClearMethodPrefix) ||
prefix == kSetMethodPrefix || prefix == kHasMethodPrefix) {
return absl::StrCat(field_name, "_");
}
}
}
}
return upbc::ResolveKeywordConflict(std::string(field_name));
}
} // namespace protos_generator

@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_GENERATOR_ACCESSORS_H_
#define UPB_PROTOS_GENERATOR_ACCESSORS_H_
#include "google/protobuf/descriptor.h"
#include "protos_generator/gen_utils.h"
#include "protos_generator/output.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc,
Output& output);
void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output);
void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc,
MessageClassType handle_type, Output& output);
void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc,
Output& output);
} // namespace protos_generator
#endif // UPB_PROTOS_GENERATOR_ACCESSORS_H_

@ -0,0 +1,144 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/gen_enums.h"
#include <algorithm>
#include <limits>
#include <string>
#include <vector>
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/descriptor.h"
#include "protos_generator/gen_utils.h"
#include "protos_generator/names.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
// Convert enum value to C++ literal.
//
// In C++, an value of -2147483648 gets interpreted as the negative of
// 2147483648, and since 2147483648 can't fit in an integer, this produces a
// compiler warning. This works around that issue.
std::string EnumInt32ToString(int number) {
if (number == std::numeric_limits<int32_t>::min()) {
// This needs to be special-cased, see explanation here:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661
return absl::StrCat(number + 1, " - 1");
} else {
return absl::StrCat(number);
}
}
std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor) {
auto containing_type = enum_descriptor->containing_type();
if (containing_type == nullptr) {
// enums types with no package name are prefixed with protos_ to prevent
// conflicts with generated C headers.
if (enum_descriptor->file()->package().empty()) {
return absl::StrCat(kNoPackageNamePrefix,
ToCIdent(enum_descriptor->name()));
}
return ToCIdent(enum_descriptor->name());
} else {
// Since the enum is in global name space (no package), it will have the
// same classified name as the C header include, to prevent collision
// rename as above.
if (containing_type->file()->package().empty()) {
return ToCIdent(absl::StrCat(containing_type->name(), "_",
kNoPackageNamePrefix,
enum_descriptor->name()));
} else {
return ToCIdent(
absl::StrCat(containing_type->name(), "_", enum_descriptor->name()));
}
}
}
std::string EnumValueSymbolInNameSpace(
const protobuf::EnumDescriptor* desc,
const protobuf::EnumValueDescriptor* value) {
auto containing_type = desc->containing_type();
if (containing_type != nullptr) {
return ToCIdent(absl::StrCat(containing_type->name(), "_", desc->name(),
"_", value->name()));
} else {
// protos enum values with no package name are prefixed with protos_ to
// prevent conflicts with generated C headers.
if (desc->file()->package().empty()) {
return absl::StrCat(kNoPackageNamePrefix, ToCIdent(value->name()));
}
return ToCIdent(value->name());
}
}
void WriteEnumValues(const protobuf::EnumDescriptor* desc, Output& output) {
std::vector<const protobuf::EnumValueDescriptor*> values;
auto value_count = desc->value_count();
values.reserve(value_count);
for (int i = 0; i < value_count; i++) {
values.push_back(desc->value(i));
}
std::sort(values.begin(), values.end(),
[](const protobuf::EnumValueDescriptor* a,
const protobuf::EnumValueDescriptor* b) {
return a->number() < b->number();
});
for (size_t i = 0; i < values.size(); i++) {
auto value = values[i];
output(" $0", EnumValueSymbolInNameSpace(desc, value));
output(" = $0", EnumInt32ToString(value->number()));
if (i != values.size() - 1) {
output(",");
}
output("\n");
}
}
void WriteEnumDeclarations(
const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
for (auto enumdesc : enums) {
output("enum $0 : int {\n", EnumTypeName(enumdesc));
WriteEnumValues(enumdesc, output);
output("};\n\n");
}
}
void WriteHeaderEnumForwardDecls(
std::vector<const protobuf::EnumDescriptor*>& enums, Output& output) {
for (const auto* enumdesc : enums) {
output("enum $0 : int;\n", EnumTypeName(enumdesc));
}
}
} // namespace protos_generator

@ -0,0 +1,52 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_GENERATOR_ENUMS_H_
#define UPB_PROTOS_GENERATOR_ENUMS_H_
#include "google/protobuf/descriptor.h"
#include "protos_generator/output.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
std::string EnumTypeName(const protobuf::EnumDescriptor* enum_descriptor);
std::string EnumValueSymbolInNameSpace(
const protobuf::EnumDescriptor* desc,
const protobuf::EnumValueDescriptor* value);
void WriteHeaderEnumForwardDecls(
std::vector<const protobuf::EnumDescriptor*>& enums, Output& output);
void WriteEnumDeclarations(
const std::vector<const protobuf::EnumDescriptor*>& enums, Output& output);
} // namespace protos_generator
#endif // UPB_PROTOS_GENERATOR_ENUMS_H_

@ -0,0 +1,117 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/gen_extensions.h"
#include "absl/strings/str_cat.h"
#include "protos_generator/gen_utils.h"
#include "protos_generator/names.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
std::string ExtensionIdentifierBase(const protobuf::FieldDescriptor* ext) {
assert(ext->is_extension());
std::string ext_scope;
if (ext->extension_scope()) {
return MessageName(ext->extension_scope());
} else {
return ToCIdent(ext->file()->package());
}
}
std::string ContainingTypeName(const protobuf::FieldDescriptor* ext) {
return ext->containing_type()->file() != ext->file()
? QualifiedClassName(ext->containing_type())
: ClassName(ext->containing_type());
}
void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext,
Output& output) {
std::string mini_table_name =
absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext");
if (ext->extension_scope()) {
output(
R"cc(
static const ::protos::internal::ExtensionIdentifier<$0, $1> $2;
)cc",
ContainingTypeName(ext), CppTypeParameterName(ext), ext->name());
} else {
output(
R"cc(
extern const ::protos::internal::ExtensionIdentifier<$0, $1> $2;
)cc",
ContainingTypeName(ext), CppTypeParameterName(ext), ext->name());
}
}
void WriteExtensionIdentifiersHeader(
const std::vector<const protobuf::FieldDescriptor*>& extensions,
Output& output) {
for (const auto* ext : extensions) {
if (!ext->extension_scope()) {
WriteExtensionIdentifierHeader(ext, output);
}
}
}
void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext,
Output& output) {
std::string mini_table_name =
absl::StrCat(ExtensionIdentifierBase(ext), "_", ext->name(), "_ext");
if (ext->extension_scope()) {
output(
R"cc(
const ::protos::internal::ExtensionIdentifier<$0, $3> $4::$2(&$1);
)cc",
ContainingTypeName(ext), mini_table_name, ext->name(),
CppTypeParameterName(ext), ClassName(ext->extension_scope()));
} else {
output(
R"cc(
const ::protos::internal::ExtensionIdentifier<$0, $3> $2(&$1);
)cc",
ContainingTypeName(ext), mini_table_name, ext->name(),
CppTypeParameterName(ext));
}
}
void WriteExtensionIdentifiers(
const std::vector<const protobuf::FieldDescriptor*>& extensions,
Output& output) {
for (const auto* ext : extensions) {
if (!ext->extension_scope()) {
WriteExtensionIdentifier(ext, output);
}
}
}
} // namespace protos_generator

@ -0,0 +1,54 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
#define UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_
#include "google/protobuf/descriptor.h"
#include "protos_generator/output.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
void WriteExtensionIdentifiersHeader(
const std::vector<const protobuf::FieldDescriptor*>& extensions,
Output& output);
void WriteExtensionIdentifierHeader(const protobuf::FieldDescriptor* ext,
Output& output);
void WriteExtensionIdentifiers(
const std::vector<const protobuf::FieldDescriptor*>& extensions,
Output& output);
void WriteExtensionIdentifier(const protobuf::FieldDescriptor* ext,
Output& output);
} // namespace protos_generator
#endif // UPB_PROTOS_GENERATOR_GEN_EXTENSIONS_H_

@ -0,0 +1,511 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/gen_messages.h"
#include <string>
#include <vector>
#include "google/protobuf/descriptor.pb.h"
#include "absl/strings/str_cat.h"
#include "google/protobuf/descriptor.h"
#include "protos_generator/gen_accessors.h"
#include "protos_generator/gen_enums.h"
#include "protos_generator/gen_extensions.h"
#include "protos_generator/gen_utils.h"
#include "protos_generator/names.h"
#include "protos_generator/output.h"
#include "upbc/common.h"
#include "upbc/file_layout.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
Output& output);
void WriteModelPublicDeclaration(
const protobuf::Descriptor* descriptor,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
const std::vector<const protobuf::EnumDescriptor*>& file_enums,
Output& output);
void WriteExtensionIdentifiersInClassHeader(
const protobuf::Descriptor* message,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
Output& output);
void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
Output& output);
void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
Output& output);
void WriteInternalForwardDeclarationsInHeader(
const protobuf::Descriptor* message, Output& output);
void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
Output& output);
void WriteExtensionIdentifiersImplementation(
const protobuf::Descriptor* message,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
Output& output);
void WriteUsingEnumsInHeader(
const protobuf::Descriptor* message,
const std::vector<const protobuf::EnumDescriptor*>& file_enums,
Output& output);
// Writes message class declarations into .upb.proto.h.
//
// For each proto Foo, FooAccess and FooProxy/FooCProxy are generated
// that are exposed to users as Foo , Ptr<Foo> and Ptr<const Foo>.
void WriteMessageClassDeclarations(
const protobuf::Descriptor* descriptor,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
const std::vector<const protobuf::EnumDescriptor*>& file_enums,
Output& output) {
if (IsMapEntryMessage(descriptor)) {
// Skip map entry generation. Low level accessors for maps are
// generated that don't require a separate map type.
return;
}
// Forward declaration of Proto Class for GCC handling of free friend method.
output("class $0;\n", ClassName(descriptor));
output("namespace internal {\n\n");
WriteModelAccessDeclaration(descriptor, output);
output("\n");
WriteInternalForwardDeclarationsInHeader(descriptor, output);
output("\n");
output("} // namespace internal\n\n");
WriteModelPublicDeclaration(descriptor, file_exts, file_enums, output);
output("namespace internal {\n");
WriteModelCProxyDeclaration(descriptor, output);
WriteModelProxyDeclaration(descriptor, output);
output("} // namespace internal\n\n");
}
void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
Output& output) {
output(
R"cc(
class $0Access {
public:
$0Access() {}
$0Access($1* msg, upb_Arena* arena) : msg_(msg), arena_(arena) {
assert(arena != nullptr);
} // NOLINT
$0Access(const $1* msg, upb_Arena* arena)
: msg_(const_cast<$1*>(msg)), arena_(arena) {
assert(arena != nullptr);
} // NOLINT
void* GetInternalArena() const { return arena_; }
)cc",
ClassName(descriptor), MessageName(descriptor));
WriteFieldAccessorsInHeader(descriptor, output);
WriteOneofAccessorsInHeader(descriptor, output);
output.Indent();
output(
R"cc(
private:
friend class $2;
friend class $0Proxy;
friend class $0CProxy;
friend struct ::protos::internal::PrivateAccess;
$1* msg_;
upb_Arena* arena_;
)cc",
ClassName(descriptor), MessageName(descriptor),
QualifiedClassName(descriptor));
output.Outdent();
output("};\n");
}
void WriteModelPublicDeclaration(
const protobuf::Descriptor* descriptor,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
const std::vector<const protobuf::EnumDescriptor*>& file_enums,
Output& output) {
output(
R"cc(
class $0 final : private internal::$0Access {
public:
using Access = internal::$0Access;
using Proxy = internal::$0Proxy;
using CProxy = internal::$0CProxy;
$0();
$0(const $0& from);
$0& operator=(const $3& from);
$0(const CProxy& from);
$0(const Proxy& from);
$0& operator=(const CProxy& from);
$0($0&& m)
: Access(absl::exchange(m.msg_, nullptr),
absl::exchange(m.arena_, nullptr)),
owned_arena_(std::move(m.owned_arena_)) {}
$0& operator=($0&& m) {
msg_ = absl::exchange(m.msg_, nullptr);
arena_ = absl::exchange(m.arena_, nullptr);
owned_arena_ = std::move(m.owned_arena_);
return *this;
}
)cc",
ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name()),
MessageName(descriptor), QualifiedClassName(descriptor));
WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessage, output);
WriteUsingEnumsInHeader(descriptor, file_enums, output);
WriteDefaultInstanceHeader(descriptor, output);
WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output);
if (descriptor->extension_range_count()) {
// for typetrait checking
output("using ExtendableType = $0;\n", ClassName(descriptor));
}
// Note: free function friends that are templates such as ::protos::Parse
// require explicit <$2> type parameter in declaration to be able to compile
// with gcc otherwise the compiler will fail with
// "has not been declared within namespace" error. Even though there is a
// namespace qualifier, cross namespace matching fails.
output.Indent();
output(
R"cc(
static const upb_MiniTable* minitable();
using $0Access::GetInternalArena;
)cc",
ClassName(descriptor));
output("\n");
output(
R"cc(
private:
const void* msg() const { return msg_; }
void* msg() { return msg_; }
$0(upb_Message* msg, upb_Arena* arena) : $0Access() {
msg_ = ($1*)msg;
arena_ = owned_arena_.ptr();
upb_Arena_Fuse(arena_, arena);
}
::protos::Arena owned_arena_;
friend struct ::protos::internal::PrivateAccess;
friend Proxy;
friend CProxy;
friend absl::StatusOr<$2>(::protos::Parse<$2>(absl::string_view bytes,
int options));
friend absl::StatusOr<$2>(::protos::Parse<$2>(
absl::string_view bytes,
const ::protos::ExtensionRegistry& extension_registry,
int options));
friend upb_Arena* ::protos::internal::GetArena<$0>($0* message);
friend upb_Arena* ::protos::internal::GetArena<$0>(::protos::Ptr<$0> message);
friend $0(::protos::internal::MoveMessage<$0>(upb_Message* msg,
upb_Arena* arena));
)cc",
ClassName(descriptor), MessageName(descriptor),
QualifiedClassName(descriptor));
output.Outdent();
output("};\n\n");
}
void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
Output& output) {
// Foo::Proxy.
output(
R"cc(
class $0Proxy final : private internal::$0Access {
public:
$0Proxy() = delete;
$0Proxy(const $0Proxy& m) : internal::$0Access() {
msg_ = m.msg_;
arena_ = m.arena_;
}
$0Proxy($0* m) : internal::$0Access() {
msg_ = m->msg_;
arena_ = m->arena_;
}
$0Proxy operator=(const $0Proxy& m) {
msg_ = m.msg_;
arena_ = m.arena_;
return *this;
}
using $0Access::GetInternalArena;
)cc",
ClassName(descriptor));
WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
output);
output("\n");
output.Indent(1);
output(
R"cc(
private:
void* msg() const { return msg_; }
$0Proxy(void* msg, upb_Arena* arena) : internal::$0Access(($1*)msg, arena) {}
friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena));
friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>(
upb_Message*, upb_Arena*));
friend struct ::protos::internal::PrivateAccess;
friend class RepeatedFieldProxy;
friend class $0CProxy;
friend class $0Access;
friend class ::protos::Ptr<$0>;
friend class ::protos::Ptr<const $0>;
static const upb_MiniTable* minitable() { return $0::minitable(); }
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>(
const $0Proxy* message);
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0Proxy>(
::protos::Ptr<$0Proxy> message);
friend upb_Arena* ::protos::internal::GetArena<$2>($2* message);
friend upb_Arena* ::protos::internal::GetArena<$2>(::protos::Ptr<$2> message);
friend $0Proxy(::protos::CloneMessage(::protos::Ptr<$2> message,
::upb::Arena& arena));
static void Rebind($0Proxy& lhs, const $0Proxy& rhs) {
lhs.msg_ = rhs.msg_;
lhs.arena_ = rhs.arena_;
}
)cc",
ClassName(descriptor), MessageName(descriptor),
QualifiedClassName(descriptor));
output.Outdent(1);
output("};\n\n");
}
void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
Output& output) {
// Foo::CProxy.
output(
R"cc(
class $0CProxy final : private internal::$0Access {
public:
$0CProxy() = delete;
$0CProxy(const $0* m)
: internal::$0Access(m->msg_, ::protos::internal::GetArena(m)) {}
$0CProxy($0Proxy m);
using $0Access::GetInternalArena;
)cc",
ClassName(descriptor), MessageName(descriptor));
WriteUsingAccessorsInHeader(descriptor, MessageClassType::kMessageProxy,
output);
output.Indent(1);
output(
R"cc(
private:
using AsNonConst = $0Proxy;
const void* msg() const { return msg_; }
$0CProxy(void* msg, upb_Arena* arena) : internal::$0Access(($1*)msg, arena){};
friend $0::CProxy(::protos::internal::CreateMessage<$0>(
upb_Message* msg, upb_Arena* arena));
friend struct ::protos::internal::PrivateAccess;
friend class RepeatedFieldProxy;
friend class ::protos::Ptr<$0>;
friend class ::protos::Ptr<const $0>;
static const upb_MiniTable* minitable() { return $0::minitable(); }
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>(
const $0CProxy* message);
friend const upb_MiniTable* ::protos::internal::GetMiniTable<$0CProxy>(
::protos::Ptr<$0CProxy> message);
static void Rebind($0CProxy& lhs, const $0CProxy& rhs) {
lhs.msg_ = rhs.msg_;
lhs.arena_ = rhs.arena_;
}
)cc",
ClassName(descriptor), MessageName(descriptor));
output.Outdent(1);
output("};\n\n");
}
void WriteDefaultInstanceHeader(const protobuf::Descriptor* message,
Output& output) {
output(" static ::protos::Ptr<const $0> default_instance();\n",
ClassName(message));
}
void WriteMessageImplementation(
const protobuf::Descriptor* descriptor,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
Output& output) {
bool message_is_map_entry = descriptor->options().map_entry();
if (!message_is_map_entry) {
// Constructor.
output(
R"cc(
$0::$0() : $0Access() {
arena_ = owned_arena_.ptr();
msg_ = $1_new(arena_);
}
$0::$0(const $0& from) : $0Access() {
arena_ = owned_arena_.ptr();
msg_ = ($1*)upb_Message_DeepClone(from.msg_, &$2, arena_);
}
$0::$0(const CProxy& from) : $0Access() {
arena_ = owned_arena_.ptr();
msg_ = ($1*)upb_Message_DeepClone(
::protos::internal::GetInternalMsg(&from), &$2, arena_);
}
$0::$0(const Proxy& from) : $0(static_cast<const CProxy&>(from)) {}
internal::$0CProxy::$0CProxy($0Proxy m) : $0Access() {
arena_ = m.arena_;
msg_ = ($1*)::protos::internal::GetInternalMsg(&m);
}
$0& $0::operator=(const $3& from) {
arena_ = owned_arena_.ptr();
msg_ = ($1*)upb_Message_DeepClone(from.msg_, &$2, arena_);
return *this;
}
$0& $0::operator=(const CProxy& from) {
arena_ = owned_arena_.ptr();
msg_ = ($1*)upb_Message_DeepClone(
::protos::internal::GetInternalMsg(&from), &$2, arena_);
return *this;
}
)cc",
ClassName(descriptor), MessageName(descriptor),
::upbc::MessageInit(descriptor->full_name()),
QualifiedClassName(descriptor));
output("\n");
// Minitable
output(
R"cc(
const upb_MiniTable* $0::minitable() { return &$1; }
)cc",
ClassName(descriptor), ::upbc::MessageInit(descriptor->full_name()));
output("\n");
}
WriteAccessorsInSource(descriptor, output);
if (!message_is_map_entry) {
output(
R"cc(
struct $0DefaultTypeInternal {
$1* msg;
upb_Arena* arena;
};
static $0DefaultTypeInternal _$0DefaultTypeBuilder() {
upb_Arena* arena = upb_Arena_New();
return $0DefaultTypeInternal{$1_new(arena), arena};
}
$0DefaultTypeInternal _$0_default_instance_ = _$0DefaultTypeBuilder();
)cc",
ClassName(descriptor), MessageName(descriptor));
output(
R"cc(
::protos::Ptr<const $0> $0::default_instance() {
return ::protos::internal::CreateMessage<$0>(
(upb_Message *)_$0_default_instance_.msg,
_$0_default_instance_.arena);
}
)cc",
ClassName(descriptor));
WriteExtensionIdentifiersImplementation(descriptor, file_exts, output);
}
}
void WriteInternalForwardDeclarationsInHeader(
const protobuf::Descriptor* message, Output& output) {
// Write declaration for internal re-usable default_instance without
// leaking implementation.
output(
R"cc(
struct $0DefaultTypeInternal;
extern $0DefaultTypeInternal _$0_default_instance_;
)cc",
ClassName(message));
}
void WriteExtensionIdentifiersInClassHeader(
const protobuf::Descriptor* message,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
Output& output) {
for (auto* ext : file_exts) {
if (ext->extension_scope() &&
ext->extension_scope()->full_name() == message->full_name()) {
WriteExtensionIdentifierHeader(ext, output);
}
}
}
void WriteExtensionIdentifiersImplementation(
const protobuf::Descriptor* message,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
Output& output) {
for (auto* ext : file_exts) {
if (ext->extension_scope() &&
ext->extension_scope()->full_name() == message->full_name()) {
WriteExtensionIdentifier(ext, output);
}
}
}
void WriteUsingEnumsInHeader(
const protobuf::Descriptor* message,
const std::vector<const protobuf::EnumDescriptor*>& file_enums,
Output& output) {
for (auto* enum_descriptor : file_enums) {
std::string enum_type_name = EnumTypeName(enum_descriptor);
std::string enum_resolved_type_name =
enum_descriptor->file()->package().empty() &&
enum_descriptor->containing_type() == nullptr
? absl::StrCat(kNoPackageNamePrefix,
ToCIdent(enum_descriptor->name()))
: enum_type_name;
if (enum_descriptor->containing_type() == nullptr ||
enum_descriptor->containing_type()->full_name() !=
message->full_name()) {
continue;
}
output("using $0", enum_descriptor->name());
if (enum_descriptor->options().deprecated()) {
output(" ABSL_DEPRECATED(\"Proto enum $0\")", enum_descriptor->name());
}
output(" = $0;", enum_resolved_type_name);
output("\n");
int value_count = enum_descriptor->value_count();
for (int i = 0; i < value_count; i++) {
output("static constexpr $0 $1", enum_descriptor->name(),
enum_descriptor->value(i)->name());
if (enum_descriptor->options().deprecated() ||
enum_descriptor->value(i)->options().deprecated()) {
output(" ABSL_DEPRECATED(\"Proto enum value $0\") ",
enum_descriptor->value(i)->name());
}
output(" = $0;\n", EnumValueSymbolInNameSpace(enum_descriptor,
enum_descriptor->value(i)));
}
}
}
} // namespace protos_generator

@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
#define UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_
#include "google/protobuf/descriptor.h"
#include "protos_generator/output.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
void WriteMessageClassDeclarations(
const protobuf::Descriptor* descriptor,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
const std::vector<const protobuf::EnumDescriptor*>& file_enums,
Output& output);
void WriteMessageImplementation(
const protobuf::Descriptor* descriptor,
const std::vector<const protobuf::FieldDescriptor*>& file_exts,
Output& output);
} // namespace protos_generator
#endif // UPB_PROTOS_GENERATOR_GEN_MESSAGES_H_

@ -0,0 +1,347 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/gen_repeated_fields.h"
#include <string>
#include <vector>
#include "google/protobuf/descriptor.pb.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor.h"
#include "protos_generator/gen_accessors.h"
#include "protos_generator/gen_enums.h"
#include "protos_generator/gen_extensions.h"
#include "protos_generator/gen_utils.h"
#include "protos_generator/names.h"
#include "protos_generator/output.h"
#include "upbc/common.h"
#include "upbc/file_layout.h"
#include "upbc/names.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
// Adds using accessors to reuse base Access class members from a Proxy/CProxy.
void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field,
absl::string_view class_name,
absl::string_view resolved_field_name,
Output& output, bool read_only) {
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
output(
R"cc(
using $0Access::$1;
using $0Access::$1_size;
)cc",
class_name, resolved_field_name);
if (!read_only) {
output(
R"cc(
using $0Access::add_$1;
using $0Access::mutable_$1;
)cc",
class_name, resolved_field_name);
}
} else {
output(
R"cc(
using $0Access::$1;
using $0Access::$1_size;
)cc",
class_name, resolved_field_name);
if (!read_only) {
output(
R"cc(
using $0Access::add_$1;
using $0Access::mutable_$1;
using $0Access::resize_$1;
using $0Access::set_$1;
)cc",
class_name, resolved_field_name);
}
}
}
void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view resolved_upbc_name,
Output& output) {
output(
R"cc(
inline size_t $1_size() const {
size_t len;
$0_$2(msg_, &len);
return len;
}
)cc",
MessageName(desc), resolved_field_name, resolved_upbc_name);
if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
output(
R"cc(
$1 $2(size_t index) const;
const ::protos::RepeatedField<const $4>::CProxy $2() const;
::protos::Ptr<::protos::RepeatedField<$4>> mutable_$2();
absl::StatusOr<$0> add_$2();
$0 mutable_$2(size_t index) const;
)cc",
MessagePtrConstType(field, /* const */ false), // $0
MessagePtrConstType(field, /* const */ true), // $1
resolved_field_name, // $2
resolved_upbc_name, // $3
MessageBaseType(field, /* maybe_const */ false) // $4
);
} else if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
output(
R"cc(
$0 $1(size_t index) const;
const ::protos::RepeatedField<$0>::CProxy $1() const;
::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1();
bool add_$1($0 val);
void set_$1(size_t index, $0 val);
bool resize_$1(size_t len);
)cc",
CppConstType(field), resolved_field_name);
} else {
output(
R"cc(
$0 $1(size_t index) const;
const ::protos::RepeatedField<$0>::CProxy $1() const;
::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1();
bool add_$1($0 val);
void set_$1(size_t index, $0 val);
bool resize_$1(size_t len);
)cc",
CppConstType(field), resolved_field_name);
}
}
void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
const absl::string_view resolved_field_name,
const absl::string_view class_name,
Output& output) {
const char arena_expression[] = "arena_";
absl::string_view upbc_name = field->name();
output(
R"cc(
$1 $0::$2(size_t index) const {
size_t len;
auto* ptr = $3_$5(msg_, &len);
assert(index < len);
return ::protos::internal::CreateMessage<$4>(
(upb_Message*)*(ptr + index), arena_);
}
)cc",
class_name, MessagePtrConstType(field, /* is_const */ true),
resolved_field_name, MessageName(message),
MessageBaseType(field, /* maybe_const */ false), upbc_name);
output(
R"cc(
absl::StatusOr<$1> $0::add_$2() {
auto new_msg = $3_add_$6(msg_, $5);
if (!new_msg) {
return ::protos::MessageAllocationError();
}
return ::protos::internal::CreateMessageProxy<$4>((upb_Message*)new_msg, $5);
}
)cc",
class_name, MessagePtrConstType(field, /* const */ false),
resolved_field_name, MessageName(message),
MessageBaseType(field, /* maybe_const */ false), arena_expression,
upbc_name);
output(
R"cc(
$1 $0::mutable_$2(size_t index) const {
size_t len;
auto* ptr = $3_$6(msg_, &len);
assert(index < len);
return ::protos::internal::CreateMessageProxy<$4>(
(upb_Message*)*(ptr + index), $5);
}
)cc",
class_name, MessagePtrConstType(field, /* is_const */ false),
resolved_field_name, MessageName(message),
MessageBaseType(field, /* maybe_const */ false), arena_expression,
upbc_name);
output(
R"cc(
const ::protos::RepeatedField<const $1>::CProxy $0::$2() const {
size_t size;
const upb_Array* arr = _$3_$4_$5(msg_, &size);
return ::protos::RepeatedField<const $1>::CProxy(arr, arena_);
};
::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
size_t size;
upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
}
)cc",
class_name, // $0
MessageBaseType(field, /* maybe_const */ false), // $1
resolved_field_name, // $2
MessageName(message), // $3
upbc_name, // $4
upbc::kRepeatedFieldArrayGetterPostfix, // $5
upbc::kRepeatedFieldMutableArrayGetterPostfix // $6
);
}
void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
const absl::string_view resolved_field_name,
const absl::string_view class_name,
Output& output) {
absl::string_view upbc_name = field->name();
output(
R"cc(
$1 $0::$2(size_t index) const {
size_t len;
auto* ptr = $3_mutable_$4(msg_, &len);
assert(index < len);
return ::protos::UpbStrToStringView(*(ptr + index));
}
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
bool $0::resize_$1(size_t len) {
return $2_resize_$3(msg_, len, arena_);
}
)cc",
class_name, resolved_field_name, MessageName(message), upbc_name);
output(
R"cc(
bool $0::add_$2($1 val) {
return $3_add_$4(msg_, ::protos::UpbStrFromStringView(val, arena_), arena_);
}
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
void $0::set_$2(size_t index, $1 val) {
size_t len;
auto* ptr = $3_mutable_$4(msg_, &len);
assert(index < len);
*(ptr + index) = ::protos::UpbStrFromStringView(val, arena_);
}
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
const ::protos::RepeatedField<$1>::CProxy $0::$2() const {
size_t size;
const upb_Array* arr = _$3_$4_$5(msg_, &size);
return ::protos::RepeatedField<$1>::CProxy(arr, arena_);
};
::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
size_t size;
upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
}
)cc",
class_name, // $0
CppConstType(field), // $1
resolved_field_name, // $2
MessageName(message), // $3
upbc_name, // $4
upbc::kRepeatedFieldArrayGetterPostfix, // $5
upbc::kRepeatedFieldMutableArrayGetterPostfix // $6
);
}
void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
const absl::string_view resolved_field_name,
const absl::string_view class_name,
Output& output) {
absl::string_view upbc_name = field->name();
output(
R"cc(
$1 $0::$2(size_t index) const {
size_t len;
auto* ptr = $3_mutable_$4(msg_, &len);
assert(index < len);
return *(ptr + index);
}
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
bool $0::resize_$1(size_t len) {
return $2_resize_$3(msg_, len, arena_);
}
)cc",
class_name, resolved_field_name, MessageName(message), upbc_name);
output(
R"cc(
bool $0::add_$2($1 val) { return $3_add_$4(msg_, val, arena_); }
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
void $0::set_$2(size_t index, $1 val) {
size_t len;
auto* ptr = $3_mutable_$4(msg_, &len);
assert(index < len);
*(ptr + index) = val;
}
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
const ::protos::RepeatedField<$1>::CProxy $0::$2() const {
size_t size;
const upb_Array* arr = _$3_$4_$5(msg_, &size);
return ::protos::RepeatedField<$1>::CProxy(arr, arena_);
};
::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
size_t size;
upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
}
)cc",
class_name, // $0
CppConstType(field), // $1
resolved_field_name, // $2
MessageName(message), // $3
upbc_name, // $4
upbc::kRepeatedFieldArrayGetterPostfix, // $5
upbc::kRepeatedFieldMutableArrayGetterPostfix // $6
);
}
} // namespace protos_generator

@ -0,0 +1,69 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_
#define THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor.h"
#include "protos_generator/output.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field,
absl::string_view class_name,
absl::string_view resolved_field_name,
Output& output, bool read_only);
void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view resolved_upbc_name,
Output& output);
void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view class_name, Output& output);
void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view class_name, Output& output);
void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
const protobuf::FieldDescriptor* field,
absl::string_view resolved_field_name,
absl::string_view class_name, Output& output);
} // namespace protos_generator
#endif // THIRD_PARTY_UPB_PROTOS_GENERATOR_GEN_REPEATED_FIELDS_H_

@ -0,0 +1,152 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/gen_utils.h"
#include <algorithm>
#include <string>
#include <vector>
#include "absl/strings/ascii.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
void AddEnums(const protobuf::Descriptor* message,
std::vector<const protobuf::EnumDescriptor*>* enums) {
enums->reserve(enums->size() + message->enum_type_count());
for (int i = 0; i < message->enum_type_count(); i++) {
enums->push_back(message->enum_type(i));
}
for (int i = 0; i < message->nested_type_count(); i++) {
AddEnums(message->nested_type(i), enums);
}
}
std::vector<const protobuf::EnumDescriptor*> SortedEnums(
const protobuf::FileDescriptor* file) {
std::vector<const protobuf::EnumDescriptor*> enums;
enums.reserve(file->enum_type_count());
for (int i = 0; i < file->enum_type_count(); i++) {
enums.push_back(file->enum_type(i));
}
for (int i = 0; i < file->message_type_count(); i++) {
AddEnums(file->message_type(i), &enums);
}
return enums;
}
void AddMessages(const protobuf::Descriptor* message,
std::vector<const protobuf::Descriptor*>* messages) {
messages->push_back(message);
for (int i = 0; i < message->nested_type_count(); i++) {
AddMessages(message->nested_type(i), messages);
}
}
std::vector<const protobuf::Descriptor*> SortedMessages(
const protobuf::FileDescriptor* file) {
std::vector<const protobuf::Descriptor*> messages;
for (int i = 0; i < file->message_type_count(); i++) {
AddMessages(file->message_type(i), &messages);
}
return messages;
}
void AddExtensionsFromMessage(
const protobuf::Descriptor* message,
std::vector<const protobuf::FieldDescriptor*>* exts) {
for (int i = 0; i < message->extension_count(); i++) {
exts->push_back(message->extension(i));
}
for (int i = 0; i < message->nested_type_count(); i++) {
AddExtensionsFromMessage(message->nested_type(i), exts);
}
}
std::vector<const protobuf::FieldDescriptor*> SortedExtensions(
const protobuf::FileDescriptor* file) {
const int extension_count = file->extension_count();
const int message_type_count = file->message_type_count();
std::vector<const protobuf::FieldDescriptor*> ret;
ret.reserve(extension_count + message_type_count);
for (int i = 0; i < extension_count; i++) {
ret.push_back(file->extension(i));
}
for (int i = 0; i < message_type_count; i++) {
AddExtensionsFromMessage(file->message_type(i), &ret);
}
return ret;
}
std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder(
const protobuf::Descriptor* message) {
std::vector<const protobuf::FieldDescriptor*> fields;
fields.reserve(message->field_count());
for (int i = 0; i < message->field_count(); i++) {
fields.push_back(message->field(i));
}
std::sort(fields.begin(), fields.end(),
[](const protobuf::FieldDescriptor* a,
const protobuf::FieldDescriptor* b) {
return a->number() < b->number();
});
return fields;
}
std::string ToCamelCase(const std::string& input, bool lower_first) {
bool capitalize_next = !lower_first;
std::string result;
result.reserve(input.size());
for (char character : input) {
if (character == '_') {
capitalize_next = true;
} else if (capitalize_next) {
result.push_back(absl::ascii_toupper(character));
capitalize_next = false;
} else {
result.push_back(character);
}
}
// Lower-case the first letter.
if (lower_first && !result.empty()) {
result[0] = absl::ascii_tolower(result[0]);
}
return result;
}
} // namespace protos_generator

@ -0,0 +1,68 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_GENERATOR_GEN_UTILS_H_
#define UPB_PROTOS_GENERATOR_GEN_UTILS_H_
#include <string>
#include <vector>
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/descriptor.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
enum class MessageClassType {
kMessage,
kMessageCProxy,
kMessageProxy,
kMessageAccess,
};
inline bool IsMapEntryMessage(const protobuf::Descriptor* descriptor) {
return descriptor->options().map_entry();
}
std::vector<const protobuf::EnumDescriptor*> SortedEnums(
const protobuf::FileDescriptor* file);
std::vector<const protobuf::Descriptor*> SortedMessages(
const protobuf::FileDescriptor* file);
std::vector<const protobuf::FieldDescriptor*> SortedExtensions(
const protobuf::FileDescriptor* file);
std::vector<const protobuf::FieldDescriptor*> FieldNumberOrder(
const protobuf::Descriptor* message);
std::string ToCamelCase(const std::string& input, bool lower_first);
} // namespace protos_generator
#endif // UPB_PROTOS_GENERATOR_GEN_UTILS_H_

@ -0,0 +1,201 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/names.h"
#include <string>
#include "upbc/keywords.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
namespace {
std::string NamespaceFromPackageName(absl::string_view package_name) {
return absl::StrCat(absl::StrReplaceAll(package_name, {{".", "::"}}),
"::protos");
}
std::string DotsToColons(const std::string& name) {
return absl::StrReplaceAll(name, {{".", "::"}});
}
std::string Namespace(const std::string& package) {
if (package.empty()) return "";
return "::" + DotsToColons(package);
}
// Return the qualified C++ name for a file level symbol.
std::string QualifiedFileLevelSymbol(const protobuf::FileDescriptor* file,
const std::string& name) {
if (file->package().empty()) {
return absl::StrCat("::", name);
}
// Append ::protos postfix to package name.
return absl::StrCat(Namespace(file->package()), "::protos::", name);
}
std::string CppTypeInternal(const protobuf::FieldDescriptor* field,
bool is_const, bool is_type_parameter) {
std::string maybe_const = is_const ? "const " : "";
switch (field->cpp_type()) {
case protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
if (is_type_parameter) {
return absl::StrCat(maybe_const,
QualifiedClassName(field->message_type()));
} else {
return absl::StrCat(maybe_const,
QualifiedClassName(field->message_type()), "*");
}
}
case protobuf::FieldDescriptor::CPPTYPE_BOOL:
return "bool";
case protobuf::FieldDescriptor::CPPTYPE_FLOAT:
return "float";
case protobuf::FieldDescriptor::CPPTYPE_INT32:
case protobuf::FieldDescriptor::CPPTYPE_ENUM:
return "int32_t";
case protobuf::FieldDescriptor::CPPTYPE_UINT32:
return "uint32_t";
case protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
return "double";
case protobuf::FieldDescriptor::CPPTYPE_INT64:
return "int64_t";
case protobuf::FieldDescriptor::CPPTYPE_UINT64:
return "uint64_t";
case protobuf::FieldDescriptor::CPPTYPE_STRING:
return "absl::string_view";
default:
ABSL_LOG(FATAL) << "Unexpected type: " << field->cpp_type();
}
}
} // namespace
std::string ClassName(const protobuf::Descriptor* descriptor) {
const protobuf::Descriptor* parent = descriptor->containing_type();
std::string res;
// Classes in global namespace without package names are prefixed
// by protos_ to avoid collision with C compiler structs defined in
// proto.upb.h.
if ((parent && parent->file()->package().empty()) ||
descriptor->file()->package().empty()) {
res = std::string(kNoPackageNamePrefix);
}
if (parent) res += ClassName(parent) + "_";
absl::StrAppend(&res, descriptor->name());
return ::upbc::ResolveKeywordConflict(res);
}
std::string QualifiedClassName(const protobuf::Descriptor* descriptor) {
return QualifiedFileLevelSymbol(descriptor->file(), ClassName(descriptor));
}
std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor) {
return QualifiedFileLevelSymbol(
descriptor->file(), absl::StrCat("internal::", ClassName(descriptor)));
}
std::string CppSourceFilename(const google::protobuf::FileDescriptor* file) {
return StripExtension(file->name()) + ".upb.proto.cc";
}
std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file) {
return StripExtension(file->name()) + ".upb.fwd.h";
}
std::string UpbCFilename(const google::protobuf::FileDescriptor* file) {
return StripExtension(file->name()) + ".upb.h";
}
std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file) {
return StripExtension(file->name()) + ".upb.proto.h";
}
void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output) {
// Skip namespace generation if package name is not specified.
if (file->package().empty()) {
return;
}
output("namespace $0 {\n\n", NamespaceFromPackageName(file->package()));
}
void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output) {
if (file->package().empty()) {
return;
}
output("} // namespace $0\n\n", NamespaceFromPackageName(file->package()));
}
std::string CppConstType(const protobuf::FieldDescriptor* field) {
return CppTypeInternal(field, /* is_const= */ true,
/* is_type_parameter= */ false);
}
std::string CppTypeParameterName(const protobuf::FieldDescriptor* field) {
return CppTypeInternal(field, /* is_const= */ false,
/* is_type_parameter= */ true);
}
std::string MessageBaseType(const protobuf::FieldDescriptor* field,
bool is_const) {
ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
std::string maybe_const = is_const ? "const " : "";
return maybe_const + QualifiedClassName(field->message_type());
}
std::string MessagePtrConstType(const protobuf::FieldDescriptor* field,
bool is_const) {
ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
std::string maybe_const = is_const ? "const " : "";
return "::protos::Ptr<" + maybe_const +
QualifiedClassName(field->message_type()) + ">";
}
std::string MessageCProxyType(const protobuf::FieldDescriptor* field,
bool is_const) {
ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
std::string maybe_const = is_const ? "const " : "";
return maybe_const + QualifiedInternalClassName(field->message_type()) +
"CProxy";
}
std::string MessageProxyType(const protobuf::FieldDescriptor* field,
bool is_const) {
ABSL_DCHECK(field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
std::string maybe_const = is_const ? "const " : "";
return maybe_const + QualifiedInternalClassName(field->message_type()) +
"Proxy";
}
} // namespace protos_generator

@ -0,0 +1,73 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_GENERATOR_NAMES_H_
#define UPB_PROTOS_GENERATOR_NAMES_H_
#include <string>
#include "google/protobuf/descriptor.pb.h"
#include "protos_generator/output.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
inline constexpr absl::string_view kNoPackageNamePrefix = "protos_";
std::string ClassName(const protobuf::Descriptor* descriptor);
std::string QualifiedClassName(const protobuf::Descriptor* descriptor);
std::string QualifiedInternalClassName(const protobuf::Descriptor* descriptor);
std::string CppSourceFilename(const google::protobuf::FileDescriptor* file);
std::string ForwardingHeaderFilename(const google::protobuf::FileDescriptor* file);
std::string UpbCFilename(const google::protobuf::FileDescriptor* file);
std::string CppHeaderFilename(const google::protobuf::FileDescriptor* file);
void WriteStartNamespace(const protobuf::FileDescriptor* file, Output& output);
void WriteEndNamespace(const protobuf::FileDescriptor* file, Output& output);
std::string CppConstType(const protobuf::FieldDescriptor* field);
std::string CppTypeParameterName(const protobuf::FieldDescriptor* field);
std::string MessageBaseType(const protobuf::FieldDescriptor* field,
bool is_const);
// Generate protos::Ptr<const Model> to be used in accessors as public
// signatures.
std::string MessagePtrConstType(const protobuf::FieldDescriptor* field,
bool is_const);
std::string MessageCProxyType(const protobuf::FieldDescriptor* field,
bool is_const);
std::string MessageProxyType(const protobuf::FieldDescriptor* field,
bool is_const);
} // namespace protos_generator
#endif // UPB_PROTOS_GENERATOR_NAMES_H_

@ -0,0 +1,92 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protos_generator/output.h"
#include <string>
#include "absl/strings/str_replace.h"
namespace protos_generator {
namespace {
namespace protobuf = ::google::protobuf;
} // namespace
std::string StripExtension(absl::string_view fname) {
size_t lastdot = fname.find_last_of('.');
if (lastdot == std::string::npos) {
return std::string(fname);
}
return std::string(fname.substr(0, lastdot));
}
std::string ToCIdent(absl::string_view str) {
return absl::StrReplaceAll(str, {{".", "_"}, {"/", "_"}, {"-", "_"}});
}
std::string ToPreproc(absl::string_view str) {
return absl::AsciiStrToUpper(ToCIdent(str));
}
void EmitFileWarning(const protobuf::FileDescriptor* file, Output& output) {
output(
R"cc(
/* This file was generated by protos_generator (the upb C++ compiler) "
from the input
* file:
*
* $0
*
* Do not edit -- your changes will be discarded when the file is
* regenerated. */
)cc",
file->name());
output("\n");
}
std::string MessageName(const protobuf::Descriptor* descriptor) {
return ToCIdent(descriptor->full_name());
}
std::string FileLayoutName(const google::protobuf::FileDescriptor* file) {
return ToCIdent(file->name()) + "_upb_file_layout";
}
std::string CHeaderFilename(const google::protobuf::FileDescriptor* file) {
return StripExtension(file->name()) + ".upb.h";
}
std::string CSourceFilename(const google::protobuf::FileDescriptor* file) {
return StripExtension(file->name()) + ".upb.c";
}
} // namespace protos_generator

@ -0,0 +1,174 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UPB_PROTOS_GENERATOR_OUTPUT_H
#define UPB_PROTOS_GENERATOR_OUTPUT_H
#include <vector>
#include "absl/log/absl_log.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/zero_copy_stream.h"
namespace protos_generator {
class Output {
public:
Output(google::protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {}
~Output() { stream_->BackUp((int)buffer_size_); }
template <class... Arg>
void operator()(absl::string_view format, const Arg&... arg) {
Write(absl::Substitute(format, arg...));
}
// Indentation size in characters.
static constexpr size_t kIndentationSize = 2;
void Indent() { Indent(kIndentationSize); }
void Indent(size_t size) { indent_ += size; }
void Outdent() { Outdent(kIndentationSize); }
void Outdent(size_t size) {
if (indent_ < size) {
ABSL_LOG(FATAL) << "mismatched Output indent/unindent calls";
}
indent_ -= size;
}
private:
void Write(absl::string_view data) {
std::string stripped;
if (absl::StartsWith(data, "\n ")) {
size_t indent = data.substr(1).find_first_not_of(' ');
if (indent > indent_) {
indent -= indent_;
}
if (indent != absl::string_view::npos) {
// Remove indentation from all lines.
auto line_prefix = data.substr(0, indent + 1);
// The final line has an extra newline and is indented two less, eg.
// R"cc(
// UPB_INLINE $0 $1_$2(const $1 *msg) {
// return $1_has_$2(msg) ? *UPB_PTR_AT(msg, $3, $0) : $4;
// }
// )cc",
std::string last_line_prefix = std::string(line_prefix);
last_line_prefix.resize(last_line_prefix.size() - 2);
data.remove_prefix(line_prefix.size());
stripped = absl::StrReplaceAll(
data, {{line_prefix, "\n"}, {last_line_prefix, "\n"}});
data = stripped;
}
} else {
WriteIndent();
}
WriteRaw(data);
}
void WriteRaw(absl::string_view data) {
while (!data.empty()) {
RefreshOutput();
size_t to_write = std::min(data.size(), buffer_size_);
memcpy(output_buffer_, data.data(), to_write);
data.remove_prefix(to_write);
output_buffer_ += to_write;
buffer_size_ -= to_write;
}
}
void WriteIndent() {
if (indent_ == 0) {
return;
}
size_t size = indent_;
while (size > buffer_size_) {
if (buffer_size_ > 0) {
memset(output_buffer_, ' ', buffer_size_);
}
size -= buffer_size_;
buffer_size_ = 0;
RefreshOutput();
}
memset(output_buffer_, ' ', size);
output_buffer_ += size;
buffer_size_ -= size;
}
void RefreshOutput() {
while (buffer_size_ == 0) {
void* void_buffer;
int size;
if (!stream_->Next(&void_buffer, &size)) {
fprintf(stderr, "upbc: Failed to write to to output\n");
abort();
}
output_buffer_ = static_cast<char*>(void_buffer);
buffer_size_ = size;
}
}
google::protobuf::io::ZeroCopyOutputStream* stream_;
char* output_buffer_ = nullptr;
size_t buffer_size_ = 0;
// Current indentation size in characters.
size_t indent_ = 0;
friend class OutputIndenter;
};
class OutputIndenter {
public:
OutputIndenter(Output& output)
: OutputIndenter(output, Output::kIndentationSize) {}
OutputIndenter(Output& output, size_t indent_size)
: indent_size_(indent_size), output_(output) {
output.Indent(indent_size);
}
~OutputIndenter() { output_.Outdent(indent_size_); }
private:
size_t indent_size_;
Output& output_;
};
std::string StripExtension(absl::string_view fname);
std::string ToCIdent(absl::string_view str);
std::string ToPreproc(absl::string_view str);
void EmitFileWarning(const google::protobuf::FileDescriptor* file, Output& output);
std::string MessageName(const google::protobuf::Descriptor* descriptor);
std::string FileLayoutName(const google::protobuf::FileDescriptor* file);
std::string CHeaderFilename(const google::protobuf::FileDescriptor* file);
std::string CSourceFilename(const google::protobuf::FileDescriptor* file);
} // namespace protos_generator
#endif // UPB_PROTOS_GENERATOR_OUTPUT_H

@ -0,0 +1,282 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/plugin.h"
#include "google/protobuf/descriptor.h"
#include "protos_generator/gen_enums.h"
#include "protos_generator/gen_extensions.h"
#include "protos_generator/gen_messages.h"
#include "protos_generator/gen_utils.h"
#include "protos_generator/names.h"
#include "protos_generator/output.h"
#include "upbc/file_layout.h"
namespace protos_generator {
namespace {
namespace protoc = ::google::protobuf::compiler;
namespace protobuf = ::google::protobuf;
using FileDescriptor = ::google::protobuf::FileDescriptor;
void WriteSource(const protobuf::FileDescriptor* file, Output& output,
bool fasttable_enabled);
void WriteHeader(const protobuf::FileDescriptor* file, Output& output);
void WriteForwardingHeader(const protobuf::FileDescriptor* file,
Output& output);
void WriteMessageImplementations(const protobuf::FileDescriptor* file,
Output& output);
void WriteTypedefForwardingHeader(
const protobuf::FileDescriptor* file,
const std::vector<const protobuf::Descriptor*>& file_messages,
Output& output);
void WriteHeaderMessageForwardDecls(
const protobuf::FileDescriptor* file,
Output& output);
class Generator : public protoc::CodeGenerator {
public:
~Generator() override {}
bool Generate(const protobuf::FileDescriptor* file,
const std::string& parameter, protoc::GeneratorContext* context,
std::string* error) const override;
uint64_t GetSupportedFeatures() const override {
return FEATURE_PROTO3_OPTIONAL;
}
};
bool Generator::Generate(const protobuf::FileDescriptor* file,
const std::string& parameter,
protoc::GeneratorContext* context,
std::string* error) const {
bool fasttable_enabled = false;
std::vector<std::pair<std::string, std::string>> params;
google::protobuf::compiler::ParseGeneratorParameter(parameter, &params);
for (const auto& pair : params) {
if (pair.first == "fasttable") {
fasttable_enabled = true;
} else {
*error = "Unknown parameter: " + pair.first;
return false;
}
}
// Write model.upb.fwd.h
Output forwarding_header_output(
context->Open(ForwardingHeaderFilename(file)));
WriteForwardingHeader(file, forwarding_header_output);
// Write model.upb.proto.h
Output header_output(context->Open(CppHeaderFilename(file)));
WriteHeader(file, header_output);
// Write model.upb.proto.cc
Output cc_output(context->Open(CppSourceFilename(file)));
WriteSource(file, cc_output, fasttable_enabled);
return true;
}
// The forwarding header defines Access/Proxy/CProxy for message classes
// used to include when referencing dependencies to prevent transitive
// dependency headers from being included.
void WriteForwardingHeader(const protobuf::FileDescriptor* file,
Output& output) {
EmitFileWarning(file, output);
output(
R"cc(
#ifndef $0_UPB_FWD_H_
#define $0_UPB_FWD_H_
)cc",
ToPreproc(file->name()));
output("\n");
for (int i = 0; i < file->public_dependency_count(); ++i) {
output("#include \"$0\"\n",
ForwardingHeaderFilename(file->public_dependency(i)));
}
if (file->public_dependency_count() > 0) {
output("\n");
}
const std::vector<const protobuf::Descriptor*> this_file_messages =
SortedMessages(file);
WriteTypedefForwardingHeader(file, this_file_messages, output);
output("#endif /* $0_UPB_FWD_H_ */\n", ToPreproc(file->name()));
}
void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
EmitFileWarning(file, output);
output(
R"cc(
#ifndef $0_UPB_PROTO_H_
#define $0_UPB_PROTO_H_
#include "protos/protos.h"
#include "protos/protos_internal.h"
#include "protos/repeated_field.h"
#include "absl/strings/string_view.h"
#include "absl/status/statusor.h"
)cc",
ToPreproc(file->name()));
// Import headers for proto public dependencies.
for (int i = 0; i < file->public_dependency_count(); i++) {
if (i == 0) {
output("// Public Imports.\n");
}
output("#include \"$0\"\n", CppHeaderFilename(file->public_dependency(i)));
if (i == file->public_dependency_count() - 1) {
output("\n");
}
}
output("#include \"upb/port/def.inc\"\n");
const std::vector<const protobuf::Descriptor*> this_file_messages =
SortedMessages(file);
const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
SortedExtensions(file);
if (!this_file_messages.empty()) {
output("\n");
}
WriteHeaderMessageForwardDecls(file, output);
WriteStartNamespace(file, output);
std::vector<const protobuf::EnumDescriptor*> this_file_enums =
SortedEnums(file);
// Write Class and Enums.
WriteEnumDeclarations(this_file_enums, output);
output("\n");
for (auto message : this_file_messages) {
WriteMessageClassDeclarations(message, this_file_exts, this_file_enums,
output);
}
output("\n");
WriteExtensionIdentifiersHeader(this_file_exts, output);
output("\n");
WriteEndNamespace(file, output);
output("\n#include \"upb/port/undef.inc\"\n\n");
// End of "C" section.
output("#endif /* $0_UPB_PROTO_H_ */\n", ToPreproc(file->name()));
}
// Writes a .upb.cc source file.
void WriteSource(const protobuf::FileDescriptor* file, Output& output,
bool fasttable_enabled) {
EmitFileWarning(file, output);
output(
R"cc(
#include <stddef.h>
#include "absl/strings/string_view.h"
#include "protos/protos.h"
#include "$0"
)cc",
CppHeaderFilename(file));
for (int i = 0; i < file->dependency_count(); i++) {
output("#include \"$0\"\n", CppHeaderFilename(file->dependency(i)));
}
output("#include \"upb/port/def.inc\"\n");
WriteStartNamespace(file, output);
WriteMessageImplementations(file, output);
const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
SortedExtensions(file);
WriteExtensionIdentifiers(this_file_exts, output);
WriteEndNamespace(file, output);
output("#include \"upb/port/undef.inc\"\n\n");
}
void WriteMessageImplementations(const protobuf::FileDescriptor* file,
Output& output) {
const std::vector<const protobuf::FieldDescriptor*> file_exts =
SortedExtensions(file);
const std::vector<const protobuf::Descriptor*> this_file_messages =
SortedMessages(file);
for (auto message : this_file_messages) {
WriteMessageImplementation(message, file_exts, output);
}
}
void WriteTypedefForwardingHeader(
const protobuf::FileDescriptor* file,
const std::vector<const protobuf::Descriptor*>& file_messages,
Output& output) {
WriteStartNamespace(file, output);
// Forward-declare types defined in this file.
for (auto message : file_messages) {
output(
R"cc(
class $0;
namespace internal {
class $0Access;
class $0Proxy;
class $0CProxy;
} // namespace internal
)cc",
ClassName(message));
}
output("\n");
WriteEndNamespace(file, output);
}
/// Writes includes for upb C minitables and fwd.h for transitive typedefs.
void WriteHeaderMessageForwardDecls(
const protobuf::FileDescriptor* file,
Output& output) {
// Import forward-declaration of types defined in this file.
output("#include \"$0\"\n", UpbCFilename(file));
output("#include \"$0\"\n", ForwardingHeaderFilename(file));
// Import forward-declaration of types in dependencies.
for (int i = 0; i < file->dependency_count(); ++i) {
output("#include \"$0\"\n", ForwardingHeaderFilename(file->dependency(i)));
}
output("\n");
}
} // namespace
} // namespace protos_generator
int main(int argc, char** argv) {
protos_generator::Generator generator_cc;
return google::protobuf::compiler::PluginMain(argc, argv, &generator_cc);
}

@ -0,0 +1,157 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load(
"//bazel:build_defs.bzl",
"UPB_DEFAULT_CPPOPTS",
)
load(
"//protos/bazel:upb_cc_proto_library.bzl",
"upb_cc_proto_library",
)
load(
"//bazel:upb_proto_library.bzl",
"upb_proto_library",
)
load(
"@rules_cc//cc:defs.bzl",
"cc_proto_library",
)
# begin:google_only
# package(default_applicable_licenses = ["//:license"])
# end:google_only
licenses(["notice"])
proto_library(
name = "test_model_proto",
srcs = [
"child_model.proto",
"test_enum.proto",
"test_extension.proto",
"test_model.proto",
],
)
proto_library(
name = "no_package_proto",
srcs = [
"no_package.proto",
],
)
proto_library(
name = "naming_conflict_proto",
srcs = [
"naming_conflict.proto",
],
)
proto_library(
name = "no_package_enum_user_proto",
srcs = [
"no_package_enum_user.proto",
],
deps = [":no_package_proto"],
)
upb_proto_library(
name = "test_model_upb_proto",
visibility = [
"//protos:__pkg__",
],
deps = [":test_model_proto"],
)
upb_cc_proto_library(
name = "test_model_upb_cc_proto",
visibility = ["//protos:__pkg__"],
deps = [":test_model_proto"],
)
upb_cc_proto_library(
name = "naming_conflict_upb_cc_proto",
visibility = [
"//visibility:private", # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private.
],
deps = [":naming_conflict_proto"],
)
upb_cc_proto_library(
name = "no_package_upb_cc_proto",
deps = [
":no_package_proto",
],
)
upb_cc_proto_library(
name = "no_package_enum_user_upb_cc_proto",
deps = [
":no_package_enum_user_proto",
],
)
cc_proto_library(
name = "test_model_cc_proto",
deps = [":test_model_proto"],
)
# begin:google_only
# proto_library(
# name = "legacy_name_proto",
# srcs = [
# "legacy-name.proto",
# ],
# )
#
# upb_cc_proto_library(
# name = "legacy_name_test_proto",
# visibility = [
# "//visibility:private", # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private.
# ],
# deps = [":legacy_name_proto"],
# )
# end:google_only
cc_test(
name = "test_generated_cc_code",
srcs = ["test_generated.cc"],
copts = UPB_DEFAULT_CPPOPTS,
deps = [
# begin:google_only
# ":legacy_name_test_proto",
# end:google_only
":no_package_upb_cc_proto",
":test_model_upb_cc_proto",
":test_model_upb_proto",
":naming_conflict_upb_cc_proto",
"@com_google_googletest//:gtest_main",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"//protos",
"//protos:repeated_field",
],
)

@ -0,0 +1,49 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package protos_generator.test;
import public "protos_generator/tests/test_enum.proto";
message ChildModel1 {
optional bool child_b1 = 44;
optional string child_str1 = 56;
}
message ChildModel3 {
string sub_key = 1;
bool bool1 = 2;
int32 i32 = 3;
optional string opt_str = 4;
optional bool opt_bool = 5;
optional int32 opt_i32 = 6;
}

@ -0,0 +1,40 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package protos_generator.test;
// option java_multiple_files = true;
enum LegacyEnum {
PHASE_DEFAULT = 0;
PHASE_BUSY = 1;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save