Merge pull request #13686 from protocolbuffers/merge-upb
Merge upb into the protobuf repopull/13688/head
commit
81242c57c5
426 changed files with 91759 additions and 0 deletions
@ -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 @@ |
||||
_build |
@ -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 |
@ -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"> </td></tr> |
||||
<tr><td port="i2"> </td></tr> |
||||
<tr><td port="i3"> </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, ¶ms); |
||||
|
||||
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…
Reference in new issue