mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-18 20:50:09 +08:00
refactor: migrate from protobuf to simple-protobuf (#520)
* refactor: migrate from protobuf to simple-protobuf * update go.mod * fix server * Update gRPC.cpp * Update gRPC.cpp
This commit is contained in:
parent
cab35d7864
commit
daa6c93900
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
@ -128,17 +128,6 @@ jobs:
|
||||
setup-python: true
|
||||
cache: true
|
||||
cache-key-prefix: QtCache-${{ matrix.platform }}-${{ matrix.target }}
|
||||
# ========================================================================================================= Build deps
|
||||
- name: Cache Download
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
path: libs/deps
|
||||
key: DepsCache-${{ matrix.platform }}-${{ matrix.target }}-${{ hashFiles('script/build_deps_*.sh') }}-Qt${{ matrix.qt_version }}
|
||||
- name: Build Dependencies
|
||||
shell: bash
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
run: ./script/build_deps_all.sh ${{ matrix.target }}
|
||||
# ========================================================================================================= Generate MakeFile and Build
|
||||
- name: Windows - Generate MakeFile and Build
|
||||
shell: bash
|
||||
|
||||
46
3rdparty/simple-protobuf/.clang-format
vendored
Normal file
46
3rdparty/simple-protobuf/.clang-format
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
---
|
||||
AccessModifierOffset: '-4'
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: 'true'
|
||||
AlignConsecutiveDeclarations: 'false'
|
||||
AlignOperands: 'false'
|
||||
AllowShortBlocksOnASingleLine: 'false'
|
||||
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: 'false'
|
||||
AllowShortLoopsOnASingleLine: 'false'
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: 'true'
|
||||
BinPackArguments: 'true'
|
||||
BinPackParameters: 'true'
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializersBeforeComma: 'true'
|
||||
ColumnLimit: 100
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
|
||||
Cpp11BracedListStyle: 'false'
|
||||
IndentWidth: '4'
|
||||
Language: Cpp
|
||||
NamespaceIndentation: None
|
||||
PointerAlignment: Middle
|
||||
SortIncludes: 'true'
|
||||
SpaceAfterCStyleCast: 'true'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
SpaceBeforeParens: Never
|
||||
SpaceInEmptyParentheses: 'true'
|
||||
SpacesBeforeTrailingComments: '0'
|
||||
SpacesInAngles: 'true'
|
||||
SpacesInCStyleCastParentheses: 'true'
|
||||
SpacesInContainerLiterals: 'true'
|
||||
SpacesInParentheses: 'true'
|
||||
SpacesInSquareBrackets: 'true'
|
||||
Standard: Cpp11
|
||||
TabWidth: '4'
|
||||
UseTab: Never
|
||||
...
|
||||
Language: Proto
|
||||
IndentWidth: '4'
|
||||
|
||||
...
|
||||
|
||||
8
3rdparty/simple-protobuf/.clang-tidy
vendored
Normal file
8
3rdparty/simple-protobuf/.clang-tidy
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
Checks: 'modernize-*,performance-*,readability-*,clang-abalyzer-*,bugprone-*,cppcoreguidelines-*,misc-*'
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: file
|
||||
...
|
||||
|
||||
5
3rdparty/simple-protobuf/.clangd
vendored
Normal file
5
3rdparty/simple-protobuf/.clangd
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
InlayHints:
|
||||
Designators: No
|
||||
Enabled: Yes
|
||||
ParameterNames: No
|
||||
DeducedTypes: No
|
||||
15
3rdparty/simple-protobuf/.github/FUNDING.yml
vendored
Normal file
15
3rdparty/simple-protobuf/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [tonda-kriz] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
103
3rdparty/simple-protobuf/.github/workflows/ci-linux-coverage.yml
vendored
Normal file
103
3rdparty/simple-protobuf/.github/workflows/ci-linux-coverage.yml
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
name: Code coverage report
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: write-all
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: 'pages'
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set reusable strings
|
||||
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
|
||||
id: strings
|
||||
shell: bash
|
||||
run: |
|
||||
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get install ninja-build protobuf-compiler lcov g++-14 gcc-14 bc
|
||||
echo "CXX=g++-14" >> ${GITHUB_ENV}
|
||||
echo "CC=gcc-14" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: >
|
||||
cmake -B ${{ steps.strings.outputs.build-output-dir }}
|
||||
-G Ninja .
|
||||
-DCMAKE_C_COMPILER=gcc-14
|
||||
-DCMAKE_CXX_COMPILER=g++-14
|
||||
-DSPB_PROTO_BUILD_TESTS=ON
|
||||
-DCMAKE_BUILD_TYPE=Debug
|
||||
-DSPB_PROTO_USE_COVERAGE=ON
|
||||
-S ${{ github.workspace }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --target unit_tests
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{ steps.strings.outputs.build-output-dir }}
|
||||
# Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: |
|
||||
ctest --output-on-failure
|
||||
mkdir report
|
||||
|
||||
- name: coverage report
|
||||
working-directory: ${{ steps.strings.outputs.build-output-dir }}
|
||||
run: |
|
||||
lcov --capture -o coverage.info --directory . --exclude '/usr/*' --exclude '*/test/*' -q --gcov-tool gcov-14
|
||||
coverage=$(genhtml coverage.info --include 'include/*' --output-directory report/library | grep -P 'lines\.+'| grep -oP '[0-9]+' | head -n1)
|
||||
if (( $(echo "$coverage <= 50" | bc -l) )); then
|
||||
color=red
|
||||
elif (( $(echo "$coverage > 80" | bc -l) )); then
|
||||
color=brightgreen
|
||||
else
|
||||
color=orange
|
||||
fi
|
||||
curl "https://img.shields.io/badge/library_coverage-$coverage%25-$color?logo=github" > report/library/coverage.svg
|
||||
coverage=$(genhtml coverage.info --include 'compiler/*' --output-directory report/compiler | grep -P 'lines\.+'| grep -oP '[0-9]+' | head -n1)
|
||||
if (( $(echo "$coverage <= 50" | bc -l) )); then
|
||||
color=red
|
||||
elif (( $(echo "$coverage > 80" | bc -l) )); then
|
||||
color=brightgreen
|
||||
else
|
||||
color=orange
|
||||
fi
|
||||
curl "https://img.shields.io/badge/protoc_coverage-$coverage%25-$color?logo=github" > report/compiler/coverage.svg
|
||||
|
||||
- name: Delete deployment
|
||||
uses: strumwolf/delete-deployment-environment@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
environment: github-pages
|
||||
onlyRemoveDeployments: true
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: ${{ steps.strings.outputs.build-output-dir }}/report
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
123
3rdparty/simple-protobuf/.github/workflows/ci-linux-tests.yml
vendored
Normal file
123
3rdparty/simple-protobuf/.github/workflows/ci-linux-tests.yml
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
name: Linux GPB compatibility
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }}, ${{ matrix.compiler }}-${{ matrix.version }}, ${{ matrix.build_type }}, sanitizer:${{ matrix.sanitizer }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
compiler: gcc
|
||||
build_type: Release
|
||||
version: '13'
|
||||
sanitizer: 'OFF'
|
||||
- os: ubuntu-24.04
|
||||
compiler: gcc
|
||||
build_type: Release
|
||||
version: '14'
|
||||
sanitizer: 'ON'
|
||||
- os: ubuntu-24.04
|
||||
compiler: gcc
|
||||
build_type: Release
|
||||
version: '14'
|
||||
sanitizer: 'OFF'
|
||||
- os: ubuntu-24.04
|
||||
compiler: gcc
|
||||
build_type: Debug
|
||||
version: '14'
|
||||
sanitizer: 'ON'
|
||||
- os: ubuntu-24.04
|
||||
compiler: clang
|
||||
build_type: Release
|
||||
version: '14'
|
||||
sanitizer: 'OFF'
|
||||
- os: ubuntu-24.04
|
||||
compiler: clang
|
||||
build_type: Release
|
||||
version: '15'
|
||||
sanitizer: 'OFF'
|
||||
- os: ubuntu-24.04
|
||||
compiler: clang
|
||||
build_type: Release
|
||||
version: '16'
|
||||
sanitizer: 'OFF'
|
||||
- os: ubuntu-24.04
|
||||
compiler: clang
|
||||
build_type: Release
|
||||
version: '17'
|
||||
sanitizer: 'OFF'
|
||||
- os: ubuntu-24.04
|
||||
compiler: clang
|
||||
build_type: Release
|
||||
version: '18'
|
||||
sanitizer: 'OFF'
|
||||
- os: ubuntu-24.04
|
||||
compiler: clang
|
||||
build_type: Release
|
||||
version: '18'
|
||||
sanitizer: 'ON'
|
||||
- os: ubuntu-24.04
|
||||
compiler: clang
|
||||
build_type: Debug
|
||||
version: '18'
|
||||
sanitizer: 'ON'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set reusable strings
|
||||
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
|
||||
id: strings
|
||||
shell: bash
|
||||
run: |
|
||||
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: >
|
||||
sudo apt-get install
|
||||
protobuf-compiler
|
||||
ninja-build
|
||||
- name: Install gcc
|
||||
if: matrix.compiler == 'gcc'
|
||||
run: |
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get install g++-${{ matrix.version }}
|
||||
sudo apt-get install gcc-${{ matrix.version }}
|
||||
echo "CXX=g++-${{ matrix.version }}" >> ${GITHUB_ENV}
|
||||
echo "CC=gcc-${{ matrix.version }}" >> ${GITHUB_ENV}
|
||||
- name: Install clang
|
||||
if: matrix.compiler == 'clang'
|
||||
run: |
|
||||
sudo apt-get install clang-${{ matrix.version }}
|
||||
echo "CXX=clang++-${{ matrix.version }}" >> ${GITHUB_ENV}
|
||||
echo "CC=clang-${{ matrix.version }}" >> ${GITHUB_ENV}
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: >
|
||||
cmake -B ${{ steps.strings.outputs.build-output-dir }}
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
-DSPB_PROTO_BUILD_TESTS=ON
|
||||
-DSPB_PROTO_BUILD_EXAMPLES=ON
|
||||
-DSPB_PROTO_BUILD_COMPATIBILITY_TESTS=ON
|
||||
-DSPB_PROTO_BUILD_ETL_TESTS=ON
|
||||
-DSPB_PROTO_USE_ADDRESS_SANITIZER=${{ matrix.sanitizer }}
|
||||
-DSPB_PROTO_USE_UB_SANITIZER=${{ matrix.sanitizer }}
|
||||
-S ${{ github.workspace }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{ steps.strings.outputs.build-output-dir }}
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{ steps.strings.outputs.build-output-dir }}
|
||||
run: ctest
|
||||
94
3rdparty/simple-protobuf/.github/workflows/ci-macos-tests.yml
vendored
Normal file
94
3rdparty/simple-protobuf/.github/workflows/ci-macos-tests.yml
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
name: MacOS build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }}, ${{ matrix.compiler.name || matrix.compiler }}-${{ matrix.compiler.version || 'default' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
compiler:
|
||||
- appleclang # Default AppleClang per OS
|
||||
- { name: clang, version: 14 }
|
||||
- { name: clang, version: 15 }
|
||||
- { name: clang, version: 16 }
|
||||
- { name: clang, version: 17 }
|
||||
- { name: clang, version: 18 }
|
||||
- { name: clang, version: 19 }
|
||||
- { name: gcc, version: 11 }
|
||||
- { name: gcc, version: 12 }
|
||||
#- { name: gcc, version: 13 }
|
||||
- { name: gcc, version: 14 }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set reusable strings
|
||||
id: strings
|
||||
shell: bash
|
||||
run: |
|
||||
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install Clang (if selected)
|
||||
if: matrix.compiler.name == 'clang'
|
||||
run: |
|
||||
brew install llvm@${{ matrix.compiler.version }}
|
||||
CLANG_PATH=$(brew --prefix llvm@${{ matrix.compiler.version }})
|
||||
echo "CLANG_PATH=$CLANG_PATH/bin" >> $GITHUB_ENV
|
||||
echo "$CLANG_PATH/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install GCC (if selected)
|
||||
if: matrix.compiler.name == 'gcc'
|
||||
run: |
|
||||
brew install gcc@${{ matrix.compiler.version }}
|
||||
GCC_PATH=$(brew --prefix gcc@${{ matrix.compiler.version }})
|
||||
echo "GCC_PATH=$GCC_PATH/bin" >> $GITHUB_ENV
|
||||
echo "$GCC_PATH/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
if [ "${{ matrix.compiler }}" == "appleclang" ]; then
|
||||
cmake \
|
||||
-B ${{ steps.strings.outputs.build-output-dir }} \
|
||||
-DCMAKE_C_COMPILER=/Library/Developer/CommandLineTools/usr/bin/cc \
|
||||
-DCMAKE_CXX_COMPILER=/Library/Developer/CommandLineTools/usr/bin/c++ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSPB_PROTO_BUILD_TESTS=ON \
|
||||
-DSPB_PROTO_BUILD_EXAMPLES=ON \
|
||||
-S ${{ github.workspace }}
|
||||
elif [ "${{ matrix.compiler.name }}" == "clang" ]; then
|
||||
cmake \
|
||||
-B ${{ steps.strings.outputs.build-output-dir }} \
|
||||
-DCMAKE_C_COMPILER=${{ env.CLANG_PATH }}/clang \
|
||||
-DCMAKE_CXX_COMPILER=${{ env.CLANG_PATH }}/clang++ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSPB_PROTO_BUILD_TESTS=ON \
|
||||
-DSPB_PROTO_BUILD_EXAMPLES=ON \
|
||||
-S ${{ github.workspace }}
|
||||
elif [ "${{ matrix.compiler.name }}" == "gcc" ]; then
|
||||
cmake \
|
||||
-B ${{ steps.strings.outputs.build-output-dir }} \
|
||||
-DCMAKE_C_COMPILER=${{ env.GCC_PATH }}/gcc-${{ matrix.compiler.version }} \
|
||||
-DCMAKE_CXX_COMPILER=${{ env.GCC_PATH }}/g++-${{ matrix.compiler.version }} \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSPB_PROTO_BUILD_TESTS=ON \
|
||||
-DSPB_PROTO_BUILD_EXAMPLES=ON \
|
||||
-S ${{ github.workspace }}
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{ steps.strings.outputs.build-output-dir }}
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{ steps.strings.outputs.build-output-dir }}
|
||||
run: ctest
|
||||
40
3rdparty/simple-protobuf/.github/workflows/ci-windows-tests.yml
vendored
Normal file
40
3rdparty/simple-protobuf/.github/workflows/ci-windows-tests.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: Windows build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build_cmake_windows:
|
||||
name: CMake on Windows 2022
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set reusable strings
|
||||
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
|
||||
id: strings
|
||||
shell: bash
|
||||
run: |
|
||||
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: >
|
||||
cmake -B ${{ steps.strings.outputs.build-output-dir }}
|
||||
-DSPB_PROTO_BUILD_TESTS=ON
|
||||
-DSPB_PROTO_BUILD_EXAMPLES=ON
|
||||
-S ${{ github.workspace }}
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
|
||||
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{ steps.strings.outputs.build-output-dir }}
|
||||
# Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: ctest --build-config Release
|
||||
38
3rdparty/simple-protobuf/.gitignore
vendored
Normal file
38
3rdparty/simple-protobuf/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
.cache/
|
||||
build/
|
||||
.vscode/
|
||||
coverage/
|
||||
|
||||
38
3rdparty/simple-protobuf/CMakeLists.txt
vendored
Normal file
38
3rdparty/simple-protobuf/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
cmake_minimum_required( VERSION 3.12 )
|
||||
|
||||
project(spb-proto VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
# Set the C++ standard to C++20
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
option(SPB_PROTO_BUILD_TESTS "Build tests" OFF)
|
||||
option(SPB_PROTO_BUILD_COMPATIBILITY_TESTS "Build gpb compatibility tests" OFF)
|
||||
option(SPB_PROTO_BUILD_COMPILER_TESTS "Build sprotoc tests" OFF)
|
||||
option(SPB_PROTO_BUILD_ETL_TESTS "Build etl compatibility tests" OFF)
|
||||
option(SPB_PROTO_BUILD_FUZZER_TESTS "Build fuzzers for spb-protoc, json-parser, gpb-parser" OFF)
|
||||
option(SPB_PROTO_BUILD_EXAMPLES "Build examples" OFF)
|
||||
option(SPB_PROTO_USE_CLANG_FORMAT "Enable clang format for generated code" ON)
|
||||
option(SPB_PROTO_USE_COVERAGE "Enable code coverage" OFF)
|
||||
option(SPB_PROTO_USE_ADDRESS_SANITIZER "Enable address sanitizer" OFF)
|
||||
option(SPB_PROTO_USE_UB_SANITIZER "Enable undefined behavior sanitizer" OFF)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/spb_protobuf.cmake)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/spb_compile_options.cmake)
|
||||
|
||||
# the main serialization library is header only
|
||||
add_library(spb-proto INTERFACE)
|
||||
target_include_directories(spb-proto INTERFACE include)
|
||||
target_compile_features(spb-proto INTERFACE cxx_std_20)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if(SPB_PROTO_BUILD_EXAMPLES)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
||||
if(SPB_PROTO_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
21
3rdparty/simple-protobuf/LICENSE
vendored
Normal file
21
3rdparty/simple-protobuf/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Antonin Kriz <antonin.kriz@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
263
3rdparty/simple-protobuf/README.md
vendored
Normal file
263
3rdparty/simple-protobuf/README.md
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
# simple-protobuf
|
||||
|
||||
[](https://github.com/tonda-kriz/simple-protobuf/actions/workflows/ci-linux-tests.yml)
|
||||
[](https://github.com/tonda-kriz/simple-protobuf/actions/workflows/ci-windows-tests.yml)
|
||||
[](https://github.com/tonda-kriz/simple-protobuf/actions/workflows/ci-macos-tests.yml)
|
||||
[](https://tonda-kriz.github.io/simple-protobuf/library)
|
||||
[](https://matrix.to/#/#simple-protobuf:gitter.im)
|
||||
|
||||
**simple data struct** serialization library for C++. With this library you can serialize and deserialize *POD* C++ structs directly to [JSON](https://json.org) or [protobuf](https://github.com/protocolbuffers/protobuf).
|
||||
When used together with [etl library](https://github.com/ETLCPP/etl) it doesn't need to allocate any memory, so its suitable for embedded environments (see [extensions](doc/extensions.md)).
|
||||
|
||||
```CPP
|
||||
namespace PhoneBook
|
||||
{
|
||||
struct Person {
|
||||
enum class PhoneType : int32_t {
|
||||
MOBILE = 0,
|
||||
HOME = 1,
|
||||
WORK = 2,
|
||||
};
|
||||
struct PhoneNumber {
|
||||
// phone number is always required
|
||||
etl::string<16> number;
|
||||
std::optional< PhoneType > type;
|
||||
};
|
||||
std::optional< std::string > name;
|
||||
// Unique ID number for this person.
|
||||
std::optional< int32_t > id;
|
||||
std::optional< std::string > email;
|
||||
// all registered phones
|
||||
std::vector< PhoneNumber > phones;
|
||||
};
|
||||
}// namespace PhoneBook
|
||||
|
||||
auto john = PhoneBook::Person{
|
||||
.name = "John Doe",
|
||||
.id = 1234,
|
||||
.email = "jdoe@example.com",
|
||||
};
|
||||
//- serialize john to json-string
|
||||
auto json = spb::json::serialize< std::string >( john );
|
||||
//- deserialize john from json-string
|
||||
auto person = spb::json::deserialize< PhoneBook::Person >( json );
|
||||
//- serialize john to protobuf-vector
|
||||
auto pb = spb::pb::serialize< std::vector< std::byte > >( john );
|
||||
//- deserialize john from protobuf-vector
|
||||
auto person2 = spb::pb::deserialize< PhoneBook::Person >( pb );
|
||||
//- john == person == person2
|
||||
```
|
||||
|
||||
## goal
|
||||
|
||||
goal of this library is to make [JSON](https://json.org) and [protobuf](https://github.com/protocolbuffers/protobuf) *part* of the C++ language itself.
|
||||
|
||||
## reason
|
||||
|
||||
There are literally a tons of [JSON](https://json.org) C++ libraries but most of them are designed in a way that the user needs to construct the json *Object* via some API and for serialization and deserialization there is a lot of boilerplate code like type/schema checking, `to_json`, `from_json`, macros... All this is needed to be done by the user, and it usually ends up with a conversion to some C++ struct.
|
||||
|
||||
spb works the other way around, from C++ struct to [JSON](https://json.org) or [protobuf](https://github.com/protocolbuffers/protobuf). With this approach user can focus only on the data, C++ struct, which is much more natural and spb will handle all the boring stuff like serialization/deserialization and type/schema checking.
|
||||
|
||||
## about
|
||||
|
||||
spb is an alternative implementation of [protobuf](https://github.com/protocolbuffers/protobuf) for C++. This is not an plugin for `protoc` but an **replacement** for `protoc`, so you don't need `protobuf` or `protoc` installed to use it. Serialization and deserialization to [JSON](https://json.org) or [protobuf](https://github.com/protocolbuffers/protobuf) is compatible with `protoc`, in other words, data serialized with code generated by `spb-protoc` can be deserialized by code generated by `protoc` and vice versa.
|
||||
|
||||
## usage
|
||||
|
||||
### dependencies
|
||||
|
||||
* C++ compiler (at least C++20)
|
||||
* cmake
|
||||
* std library
|
||||
* *(optional) clang-format for code formatting*
|
||||
|
||||
### cheat sheet
|
||||
|
||||
```cmake
|
||||
# add this repo to your project
|
||||
add_subdirectory(external/spb-proto)
|
||||
# compile proto files to C++
|
||||
spb_protobuf_generate(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/proto/person.proto)
|
||||
# add generated files to your project
|
||||
add_executable(myapp ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
# `spb-proto` is an interface library
|
||||
# the main purpose is to update include path of `myapp`
|
||||
target_link_libraries(myapp PUBLIC spb-proto)
|
||||
```
|
||||
|
||||
### how to use
|
||||
|
||||
1. define a schema for you data in a `person.proto` file
|
||||
|
||||
```proto
|
||||
package PhoneBook;
|
||||
|
||||
message Person {
|
||||
optional string name = 1;
|
||||
optional int32 id = 2; // Unique ID number for this person.
|
||||
optional string email = 3;
|
||||
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
|
||||
message PhoneNumber {
|
||||
required string number = 1; // phone number is always required
|
||||
optional PhoneType type = 2;
|
||||
}
|
||||
// all registered phones
|
||||
repeated PhoneNumber phones = 4;
|
||||
}
|
||||
```
|
||||
|
||||
2. compile `person.proto` with `spb-protoc` into `person.pb.h` and `person.pb.cc`
|
||||
|
||||
```cmake
|
||||
spb_protobuf_generate(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/proto/person.proto)
|
||||
```
|
||||
|
||||
*observe the beautifully generated `person.pb.h` and `person.pb.cc`*
|
||||
|
||||
```C++
|
||||
namespace PhoneBook
|
||||
{
|
||||
struct Person {
|
||||
enum class PhoneType : int32_t {
|
||||
MOBILE = 0,
|
||||
HOME = 1,
|
||||
WORK = 2,
|
||||
};
|
||||
struct PhoneNumber {
|
||||
// phone number is always required
|
||||
std::string number;
|
||||
std::optional< PhoneType > type;
|
||||
};
|
||||
std::optional< std::string > name;
|
||||
// Unique ID number for this person.
|
||||
std::optional< int32_t > id;
|
||||
std::optional< std::string > email;
|
||||
// all registered phones
|
||||
std::vector< PhoneNumber > phones;
|
||||
};
|
||||
}// namespace PhoneBook
|
||||
```
|
||||
|
||||
3. use `Person` struct natively and de/serialize to/from json/pb
|
||||
|
||||
```CPP
|
||||
#include <person.pb.h>
|
||||
|
||||
auto john = PhoneBook::Person{
|
||||
.name = "John Doe",
|
||||
.id = 1234,
|
||||
.email = "jdoe@example.com",
|
||||
};
|
||||
|
||||
auto json = spb::json::serialize( john );
|
||||
auto person = spb::json::deserialize< PhoneBook::Person >( json );
|
||||
auto pb = spb::pb::serialize( john );
|
||||
auto person2 = spb::pb::deserialize< PhoneBook::Person >( pb );
|
||||
//- john == person == person2
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
All generated messages (and enums) are using the following API [`include/spb/json.hpp`](include/spb/json.hpp) and [`include/spb/pb.hpp`](include/spb/pb.hpp)
|
||||
|
||||
```CPP
|
||||
//- serialize message via writer (all other `serialize` are just wrappers around this one)
|
||||
//- example: `auto serialized_size = spb::pb::serialize( message, my_writer );`
|
||||
auto serialize( const auto & message, spb::io::writer on_write ) -> size_t;
|
||||
|
||||
//- return size in bytes of serialized message
|
||||
//- example: `auto serialized_size = spb::pb::serialize_size( message );`
|
||||
auto serialize_size( const auto & message ) -> size_t;
|
||||
|
||||
//- serialize message into container like std::string, std::vector, ...
|
||||
//- example: `auto serialized_size = spb::pb::serialize( message, my_string );`
|
||||
template < typename Message, spb::resizable_container Container >
|
||||
auto serialize( const Message & message, Container & result ) -> size_t;
|
||||
|
||||
//- serialize message and return container like std::string, std::vector, ...
|
||||
//- example: `auto my_string = spb::pb::serialize< std::string >( message );`
|
||||
template < spb::resizable_container Container = std::string, typename Message >
|
||||
auto serialize( const Message & message ) -> Container;
|
||||
|
||||
```
|
||||
|
||||
```CPP
|
||||
//- deserialize message from reader (all other `deserialize` are just wrappers around this one)
|
||||
//- example: `spb::pb::deserialize( message, my_reader );`
|
||||
void deserialize( auto & message, spb::io::reader on_read );
|
||||
|
||||
//- deserialize message from container like std::string, std::vector, ...
|
||||
//- example: `spb::pb::deserialize( message, my_string );`
|
||||
template < typename Message, spb::size_container Container >
|
||||
void deserialize( Message & message, const Container & protobuf );
|
||||
|
||||
|
||||
//- return deserialized message from container like std::string, std::vector, ...
|
||||
//- example: `auto message = spb::pb::deserialize< Message >( my_string );`
|
||||
template < typename Message, spb::size_container Container >
|
||||
auto deserialize( const Container & protobuf ) -> Message;
|
||||
|
||||
//- return deserialized message from reader
|
||||
//- example: `auto message = spb::pb::deserialize< Message >( my_reader );`
|
||||
template < typename Message >
|
||||
auto deserialize( spb::io::reader reader ) -> Message;
|
||||
```
|
||||
|
||||
API is prefixed with `spb::json::` for **json** and `spb::pb::` for **protobuf**,
|
||||
template concepts [`spb::size_container`](include/spb/concepts.h) and [`spb::resizable_container`](include/spb/concepts.h) are defined in [`include/spb/concepts.h`](include/spb/concepts.h), [`spb::io::reader`](include/spb/io/io.hpp) and [`spb::io::writer`](include/spb/io/io.hpp) are user specified *functions* for IO, more info at [`include/io/io.hpp`](include/spb/io/io.hpp)
|
||||
|
||||
## type mapping
|
||||
|
||||
| proto type | CPP type | GPB encoding |
|
||||
|------------|-------------|-------------|
|
||||
| `bool` | `bool` | varint |
|
||||
| `float` | `float` | 4 bytes |
|
||||
| `double` | `double` | 8 bytes |
|
||||
| `int32` | `int32_t` | varint |
|
||||
| `sint32` | `int32_t` | zig-zag varint |
|
||||
| `uint32` | `uint32_t` | varint |
|
||||
| `int64` | `int64_t` | varint |
|
||||
| `sint64` | `int64_t` | zig-zag varint |
|
||||
| `uint64` | `uint64_t` | varint |
|
||||
| `fixed32` | `uint32_t` | 4 bytes |
|
||||
| `sfixed32` | `int32_t` | 4 bytes |
|
||||
| `fixed64` | `uint64_t` | 8 bytes |
|
||||
| `sfixed64` | `int64_t` | 8 bytes |
|
||||
| `string` | `std::string` | utf8 string |
|
||||
| `bytes` | `std::vector< std::byte >` | base64 encoded in json |
|
||||
|
||||
| proto type modifier | CPP type modifier | Notes |
|
||||
|---------------------|-------------|-------------|
|
||||
| `optional` | `std::optional<Message>` | |
|
||||
| `optional` | `std::unique_ptr<Message>` | if there is cyclic dependency between messages ( A -> B, B -> A )|
|
||||
| `repeated` | `std::vector<Message>` | |
|
||||
|
||||
See also [extensions](doc/extensions.md) for user specific types and advanced usage.
|
||||
|
||||
## example
|
||||
|
||||
navigate to the [example](example/) directory.
|
||||
|
||||
## status
|
||||
|
||||
* [x] Make it work
|
||||
* [x] Make it right
|
||||
* [ ] Make it fast
|
||||
|
||||
### roadmap
|
||||
|
||||
* [x] parser for proto files (supported syntax: `proto2` and `proto3`)
|
||||
* [x] compile proto message to C++ data struct
|
||||
* [x] generate json de/serializer for generated C++ data struct (serialized json has to be compatible with GPB)
|
||||
* [x] generate protobuf de/serializer for generated C++ data struct (serialized pb has to be compatible with GPB)
|
||||
|
||||
### missing features
|
||||
|
||||
* RPC is not implemented
|
||||
* extend is not implemented
|
||||
74
3rdparty/simple-protobuf/cmake/spb_compile_options.cmake
vendored
Normal file
74
3rdparty/simple-protobuf/cmake/spb_compile_options.cmake
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
if(SPB_PROTO_USE_ADDRESS_SANITIZER)
|
||||
message("-- SPB: Enable address sanitizer")
|
||||
endif()
|
||||
|
||||
if(SPB_PROTO_USE_UB_SANITIZER)
|
||||
message("-- SPB: Enable undefined behavior sanitizer")
|
||||
endif()
|
||||
|
||||
if(SPB_PROTO_USE_COVERAGE)
|
||||
find_program(GCOV gcov)
|
||||
if (NOT GCOV)
|
||||
message(FATAL "SPB: program gcov not found")
|
||||
endif()
|
||||
|
||||
find_program(LCOV lcov)
|
||||
if (NOT LCOV)
|
||||
message(FATAL "SPB: program lcov not found")
|
||||
endif()
|
||||
|
||||
find_program(GENHTML genhtml)
|
||||
if (NOT GENHTML)
|
||||
message(FATAL "SPB: program genhtml not found")
|
||||
endif()
|
||||
|
||||
message("-- SPB: Enable code coverage")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT IPO_ENABLED)
|
||||
endif()
|
||||
|
||||
function(spb_enable_warnings TARGET)
|
||||
if(MSVC)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# clang-cl doesn't support /Zc:preprocessor
|
||||
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd4996 /wd4244 /EHs -Wno-missing-field-initializers)
|
||||
else()
|
||||
# https://developercommunity.visualstudio.com/t/c2017-illegal-escape-sequence-when-using-in-a-raw/919371
|
||||
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd4996 /wd4244 /Zc:preprocessor)
|
||||
endif()
|
||||
target_compile_definitions(${TARGET} PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
else()
|
||||
target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wpedantic -Werror -Wno-missing-field-initializers)
|
||||
endif()
|
||||
endfunction(spb_enable_warnings)
|
||||
|
||||
function(spb_disable_warnings TARGET)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-declarations -Wno-deprecated "-Wno-#warnings")
|
||||
endif()
|
||||
endfunction(spb_disable_warnings)
|
||||
|
||||
function(spb_set_compile_options TARGET)
|
||||
spb_enable_warnings(${TARGET})
|
||||
|
||||
set_target_properties(${TARGET} PROPERTIES INTERPROCEDURAL_OPTIMIZATION IPO_ENABLED)
|
||||
|
||||
if(SPB_PROTO_USE_ADDRESS_SANITIZER)
|
||||
target_compile_options(${TARGET} PRIVATE -fsanitize=address)
|
||||
target_link_options(${TARGET} PRIVATE -fsanitize=address)
|
||||
endif()
|
||||
|
||||
if(SPB_PROTO_USE_UB_SANITIZER)
|
||||
target_compile_options(${TARGET} PRIVATE -fsanitize=undefined)
|
||||
target_link_options(${TARGET} PRIVATE -fsanitize=undefined)
|
||||
endif()
|
||||
|
||||
if(SPB_PROTO_USE_COVERAGE)
|
||||
target_compile_options(${TARGET} PRIVATE -fprofile-arcs -ftest-coverage)
|
||||
target_link_options(${TARGET} PRIVATE -fprofile-arcs -ftest-coverage)
|
||||
endif()
|
||||
|
||||
endfunction(spb_set_compile_options)
|
||||
56
3rdparty/simple-protobuf/cmake/spb_protobuf.cmake
vendored
Normal file
56
3rdparty/simple-protobuf/cmake/spb_protobuf.cmake
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
include(CMakeParseArguments)
|
||||
|
||||
if(SPB_PROTO_USE_CLANG_FORMAT)
|
||||
find_program(CLANG_FORMAT clang-format)
|
||||
endif()
|
||||
|
||||
function(spb_protobuf_generate SRCS HDRS)
|
||||
if (NOT ARGN)
|
||||
message(FATAL_ERROR "Error: spb_protobuf_generate() called without any proto files")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(${SRCS})
|
||||
set(${HDRS})
|
||||
|
||||
foreach (FIL ${ARGN})
|
||||
get_filename_component(FILE_NAME ${FIL} NAME_WE)
|
||||
get_filename_component(FILE_ABS ${FIL} ABSOLUTE)
|
||||
|
||||
if(TARGET spb-proto)
|
||||
# add directory with generated files to the spb-proto includes
|
||||
target_include_directories(spb-proto INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.cc")
|
||||
list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.h")
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if(SPB_PROTO_USE_CLANG_FORMAT AND CLANG_FORMAT)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.cc"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.h"
|
||||
COMMAND $<TARGET_FILE:spb-protoc> ARGS "--cpp_out=${CMAKE_CURRENT_BINARY_DIR}" ${FILE_ABS}
|
||||
COMMAND ${CLANG_FORMAT} ARGS -i "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.cc"
|
||||
COMMAND ${CLANG_FORMAT} ARGS -i "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.h"
|
||||
DEPENDS spb-protoc ${FILE_ABS}
|
||||
COMMENT "Compiling protofile ${FIL}"
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.cc"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.pb.h"
|
||||
COMMAND $<TARGET_FILE:spb-protoc> ARGS "--cpp_out=${CMAKE_CURRENT_BINARY_DIR}" ${FILE_ABS}
|
||||
DEPENDS spb-protoc ${FILE_ABS}
|
||||
COMMENT "Compiling protofile ${FIL}"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
endforeach ()
|
||||
|
||||
set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
|
||||
set(${SRCS} ${${SRCS}} PARENT_SCOPE)
|
||||
set(${HDRS} ${${HDRS}} PARENT_SCOPE)
|
||||
endfunction(spb_protobuf_generate)
|
||||
284
3rdparty/simple-protobuf/doc/extensions.md
vendored
Normal file
284
3rdparty/simple-protobuf/doc/extensions.md
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
# extensions
|
||||
|
||||
All extensions to the .proto files are specified in the comments, so they are ignored by the GPB protoc and therefore compatible with GPB. Extensions are using GPB [options](https://protobuf.dev/programming-guides/proto2/#options) syntax inside of C++ [attribute](https://en.cppreference.com/w/cpp/language/attributes).
|
||||
|
||||
Example:
|
||||
|
||||
```proto
|
||||
//[[ field.type = "int16" ]]
|
||||
```
|
||||
|
||||
## int types
|
||||
|
||||
You can use also **8** and **16** bit ints (`int8`, `uint8`, `int16`, `uint16`) for fields and for enums. Warning: due to compatibility with GPB, always use types with the same sign, like `int32` and `int8`, combinations like `int32` and `uint8` are invalid.
|
||||
|
||||
### how to use int types
|
||||
|
||||
Each field has an attribute `.type`.
|
||||
|
||||
```proto
|
||||
message Person{
|
||||
//[[ field.type = "int16" ]]
|
||||
required int32 id = 2;
|
||||
}
|
||||
```
|
||||
|
||||
will be translated into...
|
||||
|
||||
```CPP
|
||||
struct Person{
|
||||
int16_t id;
|
||||
}
|
||||
```
|
||||
|
||||
## enum types
|
||||
|
||||
You can specify type of an enum (`int8`, `uint8`, `int16`, `uint16`, `int32`, `uint32`, `int64` and `uint64`)
|
||||
|
||||
### how to use enum types
|
||||
|
||||
Each enum has an attribute `.type`.
|
||||
|
||||
```proto
|
||||
//[[ enum.type = "uint8" ]]
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
```
|
||||
|
||||
will be translated into...
|
||||
|
||||
```CPP
|
||||
enum class PhoneType : uint8_t {
|
||||
MOBILE = 0,
|
||||
HOME = 1,
|
||||
WORK = 2,
|
||||
};
|
||||
```
|
||||
|
||||
Default is set to:
|
||||
|
||||
```proto
|
||||
//[[ enum.type = "int32" ]]
|
||||
```
|
||||
|
||||
You can use this attribute ...
|
||||
|
||||
- before `package`, means its set for all enums in the **whole .proto file**
|
||||
- before `message`, means its set for all enums inside one **message only**
|
||||
- before `enum`, means its set for a specific **enum only**
|
||||
|
||||
you can combine them as you want, the more specific ones will be preferred.
|
||||
|
||||
## bit-fields
|
||||
|
||||
You can use bit fields (`int8:1`, `uint8:2` ...) for [ints](#int-types)
|
||||
|
||||
### how to use bit-fields
|
||||
|
||||
Bit fields are used similar as in C with [ints](#int-types). *Remember: always use types with the same sign*
|
||||
|
||||
```proto
|
||||
message Device{
|
||||
//[[ field.type = "uint8:4" ]]
|
||||
required uint8 id_major = 2;
|
||||
//[[ field.type = "uint8:4" ]]
|
||||
required uint8 id_minor = 2;
|
||||
}
|
||||
```
|
||||
|
||||
will be translated into...
|
||||
|
||||
```CPP
|
||||
struct Device{
|
||||
uint8_t id_major : 4;
|
||||
uint8_t id_minor : 4;
|
||||
}
|
||||
```
|
||||
|
||||
## container types
|
||||
|
||||
You can use your own types for containers (`optionals`, `repeated`, `string`, `bytes`).
|
||||
|
||||
### how to use user container types
|
||||
|
||||
Each container has 2 attributes `.type` (user type) and `.include` (include header for the type).
|
||||
Container needs to satisfy a [concept](../include/spb/concepts.h).
|
||||
|
||||
| container | [concept](../include/spb/concepts.h) | notes |
|
||||
|------------|------------------------------|-------------|
|
||||
| `optional` | `proto_label_optional` | [`optional`](https://protobuf.dev/programming-guides/proto2/#field-labels) field label |
|
||||
| `repeated` | `proto_label_repeated` | [`repeated`](https://protobuf.dev/programming-guides/proto2/#field-labels) field label |
|
||||
| `string` | `proto_field_string` | fixed size [`string`](https://protobuf.dev/programming-guides/proto2/#scalar) field type |
|
||||
| `string` | `proto_field_string_resizable` | resizable [`string`](https://protobuf.dev/programming-guides/proto2/#scalar) field type |
|
||||
| `bytes` | `proto_field_bytes` | fixed size [`bytes`](https://protobuf.dev/programming-guides/proto2/#scalar) field type |
|
||||
| `bytes` | `proto_field_bytes_resizable` | resizable [`bytes`](https://protobuf.dev/programming-guides/proto2/#scalar) field type |
|
||||
|
||||
Defaults are set to:
|
||||
|
||||
```proto
|
||||
//[[ optional.type = "std::optional<$>" ]]
|
||||
//[[ optional.include = "<optional>" ]]
|
||||
|
||||
//[[ repeated.type = "std::vector<$>" ]]
|
||||
//[[ repeated.include = "<vector>" ]]
|
||||
|
||||
//[[ string.type = "std::string" ]]
|
||||
//[[ string.include = "<string>" ]]
|
||||
|
||||
//[[ bytes.type = "std::vector<$>" ]]
|
||||
//[[ bytes.include = "<vector>" ]]
|
||||
```
|
||||
|
||||
`$` will be replaced with the `value_type` of a container.
|
||||
|
||||
- for `string`, `$` (if specified) will be replaced by `char`.
|
||||
- for `bytes`, `$` (if specified) will be replaced by `std::byte`.
|
||||
- for `repeated` and `optional`, `$` will be replaced by a `field type` specified in .proto
|
||||
|
||||
You can use those attributes ...
|
||||
|
||||
- before `package`, means its set for the **whole .proto file**
|
||||
- before `message`, means its set for one **message only**
|
||||
- before `field`, means its set for one **field only**
|
||||
|
||||
you can combine them as you want, the more specific ones will be preferred.
|
||||
|
||||
#### fixed size bytes, string
|
||||
|
||||
You can use **fixed size** containers for `bytes` and `string`. They needs to satisfy concept [proto_field_bytes](../include/spb/concepts.h) or [proto_field_string](../include/spb/concepts.h)
|
||||
|
||||
```proto
|
||||
message Person{
|
||||
//[[ string.type = "std::array<$,4>" ]]
|
||||
//[[ string.include = "<array>" ]]
|
||||
optional string id = 1;
|
||||
}
|
||||
```
|
||||
|
||||
will be translated into...
|
||||
|
||||
```CPP
|
||||
struct Person{
|
||||
std::optional< std::array< char, 4 > > id;
|
||||
}
|
||||
```
|
||||
|
||||
### integration with [etl library](https://github.com/ETLCPP/etl)
|
||||
|
||||
*the whole code is in [examples](../example/)*
|
||||
|
||||
1. define a schema for you data in a `etl.proto` file
|
||||
|
||||
```proto
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package ETL.Example;
|
||||
|
||||
message DeviceStatus {
|
||||
//[[ field.type = "uint32:31"]]
|
||||
required uint32 device_id = 1;
|
||||
//[[ field.type = "uint32:1"]]
|
||||
required uint32 is_online = 2;
|
||||
required uint64 last_heartbeat = 3;
|
||||
//[[ string.type = "std::array<$,8>" ]]
|
||||
//[[ string.include = "<array>" ]]
|
||||
required string firmware_version = 4;
|
||||
//[[ string.type = "etl::string<16>" ]]
|
||||
//[[ string.include = "<etl/string.h>" ]]
|
||||
required string name = 5;
|
||||
}
|
||||
|
||||
message Command {
|
||||
//[[ enum.type = "uint8"]]
|
||||
enum CMD {
|
||||
CMD_RST = 1;
|
||||
CMD_READ = 2;
|
||||
CMD_WRITE = 3;
|
||||
CMD_TST = 4;
|
||||
}
|
||||
//[[ field.type = "uint8:4"]]
|
||||
required uint32 command_id = 1; // CMD
|
||||
//[[ field.type = "uint8:2"]]
|
||||
required uint32 arg = 2; // argument for the command
|
||||
//[[ field.type = "uint8:1"]]
|
||||
required uint32 in_flag = 3; // input flag
|
||||
//[[ field.type = "uint8:1"]]
|
||||
required uint32 out_flag = 4; // output flag
|
||||
}
|
||||
|
||||
//[[ repeated.type = "etl::vector<$,16>" ]]
|
||||
//[[ repeated.include = "<etl/vector.h>" ]]
|
||||
message CommandQueue {
|
||||
repeated Command commands = 1;
|
||||
repeated DeviceStatus statuses = 2;
|
||||
}
|
||||
```
|
||||
|
||||
2. compile `etl.proto` with `spb-protoc` into `etl.pb.h` and `etl.pb.cc`
|
||||
|
||||
```cmake
|
||||
spb_protobuf_generate(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/proto/etl.proto)
|
||||
```
|
||||
|
||||
You can combine different types for each container in a single .proto file. *In this example the `string` is represented as `etl::string< 16 >` and `std::array< char, 8 >`.*
|
||||
|
||||
```C++
|
||||
namespace ETL::Example
|
||||
{
|
||||
struct DeviceStatus
|
||||
{
|
||||
uint32_t device_id : 31;
|
||||
uint32_t is_online : 1;
|
||||
uint64_t last_heartbeat;
|
||||
std::array< char, 8 > firmware_version;
|
||||
etl::string< 16 > name;
|
||||
};
|
||||
struct Command
|
||||
{
|
||||
enum class CMD : uint8_t
|
||||
{
|
||||
CMD_RST = 1,
|
||||
CMD_READ = 2,
|
||||
CMD_WRITE = 3,
|
||||
CMD_TST = 4,
|
||||
};
|
||||
// CMD
|
||||
uint8_t command_id : 4;
|
||||
// argument for the command
|
||||
uint8_t arg : 2;
|
||||
// input flag
|
||||
uint8_t in_flag : 1;
|
||||
// output flag
|
||||
uint8_t out_flag : 1;
|
||||
};
|
||||
struct CommandQueue
|
||||
{
|
||||
etl::vector< Command, 16 > commands;
|
||||
etl::vector< DeviceStatus, 16 > statuses;
|
||||
};
|
||||
}// namespace ETL::Example
|
||||
```
|
||||
|
||||
3. use generated structs natively and de/serialize to/from json/pb
|
||||
|
||||
```CPP
|
||||
#include <etl.pb.h>
|
||||
|
||||
auto command = ETL::Example::Command{
|
||||
.command_id = uint8_t( ETL::Example::Command::CMD::CMD_READ ),
|
||||
.arg = 2,
|
||||
.in_flag = 1,
|
||||
.out_flag = 0,
|
||||
};
|
||||
|
||||
auto json = spb::json::serialize( command );
|
||||
auto pb = spb::pb::serialize( command );
|
||||
|
||||
auto cmd = spb::json::deserialize< ETL::Example::Command >( json );
|
||||
auto cmd2 = spb::pb::deserialize< ETL::Example::Command >( pb );
|
||||
//- command == cmd == cmd2
|
||||
```
|
||||
19
3rdparty/simple-protobuf/example/CMakeLists.txt
vendored
Normal file
19
3rdparty/simple-protobuf/example/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
spb_protobuf_generate(PROTO_SRCS PROTO_HDRS proto/addressbook.proto)
|
||||
spb_protobuf_generate(PROTO_ETL_SRCS PROTO_ETL_HDRS proto/etl.proto)
|
||||
|
||||
add_executable(addressbook addressbook.cpp ${PROTO_SRCS})
|
||||
target_link_libraries(addressbook PUBLIC spb-proto)
|
||||
|
||||
Include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
etl
|
||||
GIT_REPOSITORY https://github.com/ETLCPP/etl
|
||||
GIT_TAG "20.39.4"
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(etl)
|
||||
|
||||
add_executable(etl-example etl.cpp ${PROTO_ETL_SRCS})
|
||||
target_link_libraries(etl-example PUBLIC spb-proto etl::etl)
|
||||
spb_set_compile_options(etl-example)
|
||||
132
3rdparty/simple-protobuf/example/addressbook.cpp
vendored
Normal file
132
3rdparty/simple-protobuf/example/addressbook.cpp
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
//- this example is based on https://protobuf.dev/getting-started/cpptutorial/
|
||||
|
||||
#include <addressbook.pb.h>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
auto load_file( const std::filesystem::path & file_path ) -> std::string
|
||||
{
|
||||
if( !std::filesystem::exists( file_path ) )
|
||||
{
|
||||
return "{}";
|
||||
}
|
||||
const auto file_size = std::filesystem::file_size( file_path );
|
||||
auto file_content = std::string( file_size, '\0' );
|
||||
|
||||
if( auto * p_file = fopen( file_path.string( ).c_str( ), "rb" ); p_file )
|
||||
{
|
||||
const auto read = fread( file_content.data( ), 1, file_content.size( ), p_file );
|
||||
fclose( p_file );
|
||||
file_content.resize( read );
|
||||
return file_content;
|
||||
}
|
||||
|
||||
perror( file_path.string( ).c_str( ) );
|
||||
throw std::system_error( std::make_error_code( std::errc( errno ) ) );
|
||||
}
|
||||
|
||||
void save_file( const std::filesystem::path & file_path, std::string_view file_content )
|
||||
{
|
||||
if( auto * p_file = fopen( file_path.string( ).c_str( ), "wb" ); p_file )
|
||||
{
|
||||
const auto written = fwrite( file_content.data( ), 1, file_content.size( ), p_file );
|
||||
fclose( p_file );
|
||||
if( written == file_content.size( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
perror( file_path.string( ).c_str( ) );
|
||||
throw std::system_error( std::make_error_code( std::errc( errno ) ) );
|
||||
}
|
||||
// This function fills in a Person message based on user input.
|
||||
void PromptForAddress( tutorial::Person & person )
|
||||
{
|
||||
std::cout << "Enter person ID number: ";
|
||||
int id;
|
||||
std::cin >> id;
|
||||
person.id = id;
|
||||
std::cin.ignore( 256, '\n' );
|
||||
|
||||
std::cout << "Enter name: ";
|
||||
getline( std::cin, person.name.emplace( ) );
|
||||
|
||||
std::cout << "Enter email address (blank for none): ";
|
||||
std::string email;
|
||||
getline( std::cin, email );
|
||||
if( !email.empty( ) )
|
||||
{
|
||||
person.email = std::move( email );
|
||||
}
|
||||
|
||||
while( true )
|
||||
{
|
||||
std::cout << "Enter a phone number (or leave blank to finish): ";
|
||||
std::string number;
|
||||
getline( std::cin, number );
|
||||
if( number.empty( ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto & phone_number = person.phones.emplace_back( );
|
||||
phone_number.number = number;
|
||||
|
||||
std::cout << "Is this a mobile, home, or work phone? ";
|
||||
std::string type;
|
||||
getline( std::cin, type );
|
||||
if( type == "mobile" )
|
||||
{
|
||||
phone_number.type = tutorial::Person::PhoneType::PHONE_TYPE_MOBILE;
|
||||
}
|
||||
else if( type == "home" )
|
||||
{
|
||||
phone_number.type = tutorial::Person::PhoneType::PHONE_TYPE_HOME;
|
||||
}
|
||||
else if( type == "work" )
|
||||
{
|
||||
phone_number.type = tutorial::Person::PhoneType::PHONE_TYPE_WORK;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unknown phone type. Using default." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}// namespace
|
||||
|
||||
// Main function: Reads the entire address book from a file,
|
||||
// adds one person based on user input, then writes it back out to the same
|
||||
// file.
|
||||
auto main( int argc, char * argv[] ) -> int
|
||||
{
|
||||
if( argc != 2 )
|
||||
{
|
||||
std::cerr << "Usage: " << argv[ 0 ] << " ADDRESS_BOOK_FILE" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto address_book =
|
||||
spb::json::deserialize< tutorial::AddressBook >( load_file( argv[ 1 ] ) );
|
||||
|
||||
// Add an address.
|
||||
PromptForAddress( address_book.people.emplace_back( ) );
|
||||
|
||||
// Write the new address book back to disk.
|
||||
save_file( argv[ 1 ], spb::json::serialize( address_book ) );
|
||||
}
|
||||
catch( const std::exception & e )
|
||||
{
|
||||
std::cerr << "Exception: " << e.what( ) << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
3rdparty/simple-protobuf/example/etl.cpp
vendored
Normal file
22
3rdparty/simple-protobuf/example/etl.cpp
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
#include "spb/pb.hpp"
|
||||
#include <etl.pb.h>
|
||||
#include <iostream>
|
||||
|
||||
auto create_command( uint8_t arg ) -> ETL::Example::Command
|
||||
{
|
||||
return {
|
||||
.command_id = uint8_t( ETL::Example::Command::CMD::CMD_READ ),
|
||||
.arg = arg,
|
||||
.in_flag = 1,
|
||||
.out_flag = 0,
|
||||
};
|
||||
}
|
||||
|
||||
auto main( int, char *[] ) -> int
|
||||
{
|
||||
std::cout << "sizeof(Command): " << sizeof( ETL::Example::Command ) << std::endl;
|
||||
std::cout << "sizeof(DeviceStatus): " << sizeof( ETL::Example::DeviceStatus ) << std::endl;
|
||||
std::cout << "sizeof(CommandQueue): " << sizeof( ETL::Example::CommandQueue ) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
3rdparty/simple-protobuf/example/proto/addressbook.proto
vendored
Normal file
25
3rdparty/simple-protobuf/example/proto/addressbook.proto
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package tutorial;
|
||||
|
||||
message Person {
|
||||
optional string name = 1;
|
||||
optional int32 id = 2;
|
||||
optional string email = 3;
|
||||
|
||||
enum PhoneType {
|
||||
PHONE_TYPE_UNSPECIFIED = 0;
|
||||
PHONE_TYPE_MOBILE = 1;
|
||||
PHONE_TYPE_HOME = 2;
|
||||
PHONE_TYPE_WORK = 3;
|
||||
}
|
||||
|
||||
message PhoneNumber {
|
||||
optional string number = 1;
|
||||
optional PhoneType type = 2 [ default = PHONE_TYPE_HOME ];
|
||||
}
|
||||
|
||||
repeated PhoneNumber phones = 4;
|
||||
}
|
||||
|
||||
message AddressBook { repeated Person people = 1; }
|
||||
43
3rdparty/simple-protobuf/example/proto/etl.proto
vendored
Normal file
43
3rdparty/simple-protobuf/example/proto/etl.proto
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package ETL.Example;
|
||||
|
||||
message DeviceStatus {
|
||||
//[[ field.type = "uint32:31"]]
|
||||
required uint32 device_id = 1;
|
||||
//[[ field.type = "uint32:1"]]
|
||||
required uint32 is_online = 2;
|
||||
required uint64 last_heartbeat = 3;
|
||||
//[[ string.type = "std::array<$,8>" ]]
|
||||
//[[ string.include = "<array>" ]]
|
||||
required string firmware_version = 4;
|
||||
//[[ string.type = "etl::string<16>" ]]
|
||||
//[[ string.include = "<etl/string.h>" ]]
|
||||
required string name = 5;
|
||||
}
|
||||
|
||||
message Command {
|
||||
//[[ enum.type = "uint8"]]
|
||||
enum CMD {
|
||||
CMD_RST = 1;
|
||||
CMD_READ = 2;
|
||||
CMD_WRITE = 3;
|
||||
CMD_TST = 4;
|
||||
}
|
||||
//[[ field.type = "uint8:4"]]
|
||||
required uint32 command_id = 1; // CMD
|
||||
//[[ field.type = "uint8:2"]]
|
||||
required uint32 arg = 2; // argument for the command
|
||||
//[[ field.type = "uint8:1"]]
|
||||
required uint32 in_flag = 3; // input flag
|
||||
//[[ field.type = "uint8:1"]]
|
||||
required uint32 out_flag = 4; // output flag
|
||||
}
|
||||
|
||||
//[[ repeated.type = "etl::vector<$,16>" ]]
|
||||
//[[ repeated.include = "<etl/vector.h>" ]]
|
||||
message CommandQueue {
|
||||
repeated Command commands = 1;
|
||||
repeated DeviceStatus statuses = 2;
|
||||
}
|
||||
51
3rdparty/simple-protobuf/include/spb/bits.h
vendored
Normal file
51
3rdparty/simple-protobuf/include/spb/bits.h
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/***************************************************************************\
|
||||
* Name : bitfields *
|
||||
* Description : bitfields checks *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace spb::detail
|
||||
{
|
||||
template < typename T >
|
||||
concept signed_int = std::is_signed_v< T > && std::is_integral_v< T >;
|
||||
|
||||
template < typename T >
|
||||
concept unsigned_int = !std::is_signed_v< T > && std::is_integral_v< T >;
|
||||
|
||||
static inline void check_if_value_fit_in_bits( signed_int auto value, uint32_t bits )
|
||||
{
|
||||
assert( sizeof( value ) * CHAR_BIT >= bits );
|
||||
assert( bits > 0 );
|
||||
|
||||
decltype( value ) max = ( 1LL << ( bits - 1 ) ) - 1;
|
||||
decltype( value ) min = -( 1LL << ( bits - 1 ) );
|
||||
|
||||
if( ( value < min ) | ( value > max ) ) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error( "bitfield overflow" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void check_if_value_fit_in_bits( unsigned_int auto value, uint32_t bits )
|
||||
{
|
||||
assert( sizeof( value ) * CHAR_BIT >= bits );
|
||||
|
||||
decltype( value ) max = ( 1LL << bits ) - 1;
|
||||
|
||||
if( value > max ) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error( "bitfield overflow" );
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace spb::detail
|
||||
97
3rdparty/simple-protobuf/include/spb/concepts.h
vendored
Normal file
97
3rdparty/simple-protobuf/include/spb/concepts.h
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/***************************************************************************\
|
||||
* Name : template concepts *
|
||||
* Description : general template concepts used by protobuf de/serializer *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
namespace spb
|
||||
{
|
||||
template < class T >
|
||||
concept resizable_container = requires( T container ) {
|
||||
{ container.data( ) } -> std::same_as< typename std::decay_t< T >::value_type * >;
|
||||
{ container.resize( 1 ) };
|
||||
typename std::decay_t< T >::value_type;
|
||||
{ sizeof( typename std::decay_t< T >::value_type ) == sizeof( char ) };
|
||||
};
|
||||
|
||||
template < class T >
|
||||
concept size_container = requires( T container ) {
|
||||
{ container.data( ) };
|
||||
{ container.size( ) } -> std::convertible_to< std::size_t >;
|
||||
typename std::decay_t< T >::value_type;
|
||||
{ sizeof( typename std::decay_t< T >::value_type ) == sizeof( char ) };
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template < class T >
|
||||
concept proto_enum = std::is_enum_v< T >;
|
||||
|
||||
template < class T >
|
||||
concept proto_field_int_or_float = std::is_integral_v< T > || std::is_floating_point_v< T >;
|
||||
|
||||
template < class T >
|
||||
concept proto_field_number = proto_enum< T > || proto_field_int_or_float< T >;
|
||||
|
||||
template < class T >
|
||||
concept container = requires( T container ) {
|
||||
{ container.data( ) } -> std::same_as< typename std::decay_t< T >::value_type * >;
|
||||
{ container.size( ) } -> std::convertible_to< std::size_t >;
|
||||
{ container.begin( ) };
|
||||
{ container.end( ) };
|
||||
typename std::decay_t< T >::value_type;
|
||||
};
|
||||
|
||||
template < class T >
|
||||
concept proto_field_bytes =
|
||||
container< T > && std::is_same_v< typename std::decay_t< T >::value_type, std::byte >;
|
||||
|
||||
template < class T >
|
||||
concept proto_field_bytes_resizable = proto_field_bytes< T > && requires( T obj ) {
|
||||
{ obj.resize( 1 ) };
|
||||
{ obj.clear( ) };
|
||||
};
|
||||
|
||||
template < class T >
|
||||
concept proto_field_string =
|
||||
container< T > && std::is_same_v< typename std::decay_t< T >::value_type, char >;
|
||||
|
||||
template < class T >
|
||||
concept proto_field_string_resizable = proto_field_string< T > && requires( T obj ) {
|
||||
{ obj.append( "1", 1 ) };
|
||||
{ obj.clear( ) };
|
||||
};
|
||||
|
||||
template < class T >
|
||||
concept proto_label_repeated = requires( T container ) {
|
||||
{ container.emplace_back( ) };
|
||||
{ container.begin( ) };
|
||||
{ container.end( ) };
|
||||
{ container.clear( ) };
|
||||
typename T::value_type;
|
||||
} && !proto_field_string< T > && !proto_field_bytes< T >;
|
||||
|
||||
template < class T >
|
||||
concept proto_label_optional = requires( T container ) {
|
||||
{ container.has_value( ) } -> std::convertible_to< bool >;
|
||||
{ container.reset( ) };
|
||||
{ *container } -> std::same_as< typename T::value_type & >;
|
||||
{ container.emplace( typename T::value_type( ) ) } -> std::same_as< typename T::value_type & >;
|
||||
typename T::value_type;
|
||||
};
|
||||
|
||||
template < class T >
|
||||
concept proto_message = std::is_class_v< T > && !proto_field_string< T > &&
|
||||
!proto_field_bytes< T > && !proto_label_repeated< T > && !proto_label_optional< T >;
|
||||
|
||||
}// namespace detail
|
||||
}// namespace spb
|
||||
87
3rdparty/simple-protobuf/include/spb/io/buffer-io.hpp
vendored
Normal file
87
3rdparty/simple-protobuf/include/spb/io/buffer-io.hpp
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/***************************************************************************\
|
||||
* Name : buffered reader *
|
||||
* Description : buffer between io::reader and detail::istream *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "io.hpp"
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
namespace spb::io
|
||||
{
|
||||
#ifndef SPB_READ_BUFFER_SIZE
|
||||
#define SPB_READ_BUFFER_SIZE 256U
|
||||
#endif
|
||||
|
||||
class buffered_reader
|
||||
{
|
||||
private:
|
||||
io::reader on_read;
|
||||
std::array< char, SPB_READ_BUFFER_SIZE > buffer;
|
||||
size_t begin_index = 0;
|
||||
size_t end_index = 0;
|
||||
bool eof_reached = false;
|
||||
|
||||
auto bytes_in_buffer( ) const noexcept -> size_t
|
||||
{
|
||||
return end_index - begin_index;
|
||||
}
|
||||
|
||||
auto space_left_in_buffer( ) const noexcept -> size_t
|
||||
{
|
||||
return SPB_READ_BUFFER_SIZE - end_index;
|
||||
}
|
||||
|
||||
void shift_data_to_start( ) noexcept
|
||||
{
|
||||
if( begin_index > 0 )
|
||||
{
|
||||
memmove( buffer.data( ), buffer.data( ) + begin_index, bytes_in_buffer( ) );
|
||||
end_index -= begin_index;
|
||||
begin_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void read_buffer( )
|
||||
{
|
||||
shift_data_to_start( );
|
||||
|
||||
while( bytes_in_buffer( ) < SPB_READ_BUFFER_SIZE && !eof_reached )
|
||||
{
|
||||
auto bytes_in = on_read( &buffer[ end_index ], space_left_in_buffer( ) );
|
||||
eof_reached |= bytes_in == 0;
|
||||
end_index += bytes_in;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
explicit buffered_reader( io::reader reader )
|
||||
: on_read( reader )
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] auto view( size_t minimal_size ) -> std::string_view
|
||||
{
|
||||
minimal_size = std::max< size_t >( minimal_size, 1U );
|
||||
if( bytes_in_buffer( ) < minimal_size )
|
||||
{
|
||||
read_buffer( );
|
||||
}
|
||||
return std::string_view( &buffer[ begin_index ], bytes_in_buffer( ) );
|
||||
}
|
||||
|
||||
void skip( size_t size ) noexcept
|
||||
{
|
||||
begin_index += std::min( size, bytes_in_buffer( ) );
|
||||
}
|
||||
};
|
||||
|
||||
}// namespace spb::io
|
||||
69
3rdparty/simple-protobuf/include/spb/io/function_ref.hpp
vendored
Normal file
69
3rdparty/simple-protobuf/include/spb/io/function_ref.hpp
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/***************************************************************************\
|
||||
* Name : function_ref *
|
||||
* Description : non-owning reference to a callable *
|
||||
* Author : LLVM *
|
||||
* Reference : https://llvm.org/doxygen/classllvm_1_1function__ref_3_01Ret_07Params_8_8_8_08_4.html
|
||||
*
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace spb::detail
|
||||
{
|
||||
|
||||
template < typename Fn >
|
||||
class function_ref;
|
||||
|
||||
template < typename Ret, typename... Params >
|
||||
class function_ref< Ret( Params... ) >
|
||||
{
|
||||
Ret ( *callback )( intptr_t callable, Params... params ) = nullptr;
|
||||
intptr_t callable;
|
||||
|
||||
template < typename Callable >
|
||||
static Ret callback_fn( intptr_t callable, Params... params )
|
||||
{
|
||||
return ( *reinterpret_cast< Callable * >( callable ) )(
|
||||
std::forward< Params >( params )... );
|
||||
}
|
||||
|
||||
public:
|
||||
function_ref( ) = default;
|
||||
function_ref( std::nullptr_t )
|
||||
{
|
||||
}
|
||||
|
||||
template < typename Callable >
|
||||
function_ref( Callable && callable,
|
||||
// This is not the copy-constructor.
|
||||
std::enable_if_t< !std::is_same< std::remove_cvref_t< Callable >,
|
||||
function_ref >::value > * = nullptr,
|
||||
// Functor must be callable and return a suitable type.
|
||||
std::enable_if_t< std::is_void< Ret >::value ||
|
||||
std::is_convertible< decltype( std::declval< Callable >( )(
|
||||
std::declval< Params >( )... ) ),
|
||||
Ret >::value > * = nullptr )
|
||||
: callback( callback_fn< std::remove_reference_t< Callable > > )
|
||||
, callable( reinterpret_cast< intptr_t >( &callable ) )
|
||||
{
|
||||
}
|
||||
|
||||
Ret operator( )( Params... params ) const
|
||||
{
|
||||
return callback( callable, std::forward< Params >( params )... );
|
||||
}
|
||||
|
||||
explicit operator bool( ) const
|
||||
{
|
||||
return callback;
|
||||
}
|
||||
};
|
||||
}// namespace spb::detail
|
||||
39
3rdparty/simple-protobuf/include/spb/io/io.hpp
vendored
Normal file
39
3rdparty/simple-protobuf/include/spb/io/io.hpp
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/***************************************************************************\
|
||||
* Name : generic reader and writer *
|
||||
* Description : user specific input/output used for de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "function_ref.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace spb::io
|
||||
{
|
||||
/**
|
||||
* @brief generic writer used to write exactly `size` number of bytes from `p_data`
|
||||
*
|
||||
* @param[in] p_data input buffer
|
||||
* @param[in] size input buffer size
|
||||
* @throws any exception thrown will stop the `serialize` process and will be propagated to the
|
||||
* caller of `spb::pb::serialize` or `spb::json::serialize`
|
||||
*/
|
||||
using writer = spb::detail::function_ref< void( const void * p_data, size_t size ) >;
|
||||
|
||||
//-
|
||||
/**
|
||||
* @brief generic reader used to read up to `size` number of bytes into `p_data`
|
||||
*
|
||||
* @param p_data output buffer (this is never nullptr)
|
||||
* @param[in] size number of bytes to read (this is always > 0)
|
||||
* @return number of bytes copied into `p_data`, could be less than `size`. 0 indicates end-of-file
|
||||
* @throws any exception thrown will stop the `deserialize` process and will be propagated to the
|
||||
* caller of `spb::pb::deserialize` or `spb::json::deserialize`
|
||||
*/
|
||||
using reader = spb::detail::function_ref< size_t( void * p_data, size_t size ) >;
|
||||
|
||||
}// namespace spb::io
|
||||
153
3rdparty/simple-protobuf/include/spb/json.hpp
vendored
Normal file
153
3rdparty/simple-protobuf/include/spb/json.hpp
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
/***************************************************************************\
|
||||
* Name : Public API for JSON *
|
||||
* Description : all json serialize and deserialize functions *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "spb/io/io.hpp"
|
||||
#include "json/deserialize.hpp"
|
||||
#include "json/serialize.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace spb::json
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief serialize message via writer
|
||||
*
|
||||
* @param message to be serialized
|
||||
* @param on_write function for handling the writes
|
||||
* @return serialized size in bytes
|
||||
* @throws exceptions only from `on_write`
|
||||
*/
|
||||
static inline auto serialize( const auto & message, spb::io::writer on_write ) -> size_t
|
||||
{
|
||||
return detail::serialize( message, on_write );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief return json-string serialized size in bytes
|
||||
*
|
||||
* @param message to be serialized
|
||||
* @return serialized size in bytes
|
||||
*/
|
||||
[[nodiscard]] static inline auto serialize_size( const auto & message ) -> size_t
|
||||
{
|
||||
return serialize( message, spb::io::writer( nullptr ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief serialize message into json-string
|
||||
*
|
||||
* @param message to be serialized
|
||||
* @return serialized json
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto serialized = std::vector< std::byte >();`
|
||||
* `spb::json::serialize( message, serialized );`
|
||||
*/
|
||||
template < typename Message, spb::resizable_container Container >
|
||||
static inline auto serialize( const Message & message, Container & result ) -> size_t
|
||||
{
|
||||
const auto size = serialize_size( message );
|
||||
result.resize( size );
|
||||
auto writer = [ ptr = result.data( ) ]( const void * data, size_t size ) mutable
|
||||
{
|
||||
memcpy( ptr, data, size );
|
||||
ptr += size;
|
||||
};
|
||||
|
||||
serialize( message, writer );
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief serialize message into json
|
||||
*
|
||||
* @param[in] message to be serialized
|
||||
* @return serialized json
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto serialized_message = spb::json::serialize( message );`
|
||||
*/
|
||||
template < spb::resizable_container Container = std::string, typename Message >
|
||||
[[nodiscard]] static inline auto serialize( const Message & message ) -> Container
|
||||
{
|
||||
auto result = Container( );
|
||||
serialize< Message, Container >( message, result );
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize json-string into variable
|
||||
*
|
||||
* @param on_read function for handling reads
|
||||
* @param result deserialized json
|
||||
* @throws std::runtime_error on error
|
||||
*/
|
||||
static inline void deserialize( auto & result, spb::io::reader on_read )
|
||||
{
|
||||
return detail::deserialize( result, on_read );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize json-string into variable
|
||||
*
|
||||
* @param json string with json
|
||||
* @param message deserialized json
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto serialized = std::string( ... );`
|
||||
* `auto message = Message();`
|
||||
* `spb::json::deserialize( message, serialized );`
|
||||
*/
|
||||
template < typename Message, spb::size_container Container >
|
||||
static inline void deserialize( Message & message, const Container & json )
|
||||
{
|
||||
auto reader = [ ptr = json.data( ), end = json.data( ) + json.size( ) ](
|
||||
void * data, size_t size ) mutable -> size_t
|
||||
{
|
||||
size_t bytes_left = end - ptr;
|
||||
|
||||
size = std::min( size, bytes_left );
|
||||
memcpy( data, ptr, size );
|
||||
ptr += size;
|
||||
return size;
|
||||
};
|
||||
return deserialize( message, reader );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize json-string into variable
|
||||
*
|
||||
* @param json string with json
|
||||
* @return deserialized json or throw an exception
|
||||
* @example `auto serialized = std::string( ... );`
|
||||
* `auto message = spb::json::deserialize< Message >( serialized );`
|
||||
*/
|
||||
template < typename Message, spb::size_container Container >
|
||||
[[nodiscard]] static inline auto deserialize( const Container & json ) -> Message
|
||||
{
|
||||
auto message = Message{ };
|
||||
deserialize( message, json );
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize json-string into variable
|
||||
*
|
||||
* @param on_read function for handling reads
|
||||
* @return deserialized json
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto message = spb::json::deserialize< Message >( reader )`
|
||||
*/
|
||||
template < typename Message >
|
||||
[[nodiscard]] static inline auto deserialize( spb::io::reader on_read ) -> Message
|
||||
{
|
||||
auto message = Message{ };
|
||||
return deserialize( message, on_read );
|
||||
}
|
||||
|
||||
}// namespace spb::json
|
||||
217
3rdparty/simple-protobuf/include/spb/json/base64.h
vendored
Normal file
217
3rdparty/simple-protobuf/include/spb/json/base64.h
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
/***************************************************************************\
|
||||
* Name : base64 library for json *
|
||||
* Description : RFC 4648 base64 decoder and encoder *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../concepts.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace spb::json::detail
|
||||
{
|
||||
template < typename ostream >
|
||||
static inline void base64_encode( ostream & output, std::span< const std::byte > input )
|
||||
{
|
||||
static constexpr char encode_table[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
const auto * p_char = reinterpret_cast< const uint8_t * >( input.data( ) );
|
||||
//
|
||||
//- +3 means 3 bytes are being processed in one iteration (3 * 8 = 24 bits)
|
||||
//
|
||||
for( size_t idx = 3; idx <= input.size( ); idx += 3 )
|
||||
{
|
||||
auto temp = uint32_t( *p_char++ ) << 16U;
|
||||
temp += uint32_t( *p_char++ ) << 8U;
|
||||
temp += ( *p_char++ );
|
||||
output.write( encode_table[ ( temp & 0x00FC0000U ) >> 18U ] );
|
||||
output.write( encode_table[ ( temp & 0x0003F000U ) >> 12U ] );
|
||||
output.write( encode_table[ ( temp & 0x00000FC0U ) >> 6U ] );
|
||||
output.write( encode_table[ ( temp & 0x0000003FU ) ] );
|
||||
}
|
||||
switch( input.size( ) % 3 )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
auto temp = uint32_t( *p_char++ ) << 16U;
|
||||
output.write( encode_table[ ( temp & 0x00FC0000U ) >> 18U ] );
|
||||
output.write( encode_table[ ( temp & 0x0003F000U ) >> 12U ] );
|
||||
output.write( '=' );
|
||||
output.write( '=' );
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
auto temp = uint32_t( *p_char++ ) << 16U;
|
||||
temp += uint32_t( *p_char++ ) << 8U;
|
||||
output.write( encode_table[ ( temp & 0x00FC0000 ) >> 18 ] );
|
||||
output.write( encode_table[ ( temp & 0x0003F000 ) >> 12 ] );
|
||||
output.write( encode_table[ ( temp & 0x00000FC0 ) >> 6 ] );
|
||||
output.write( '=' );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template < typename istream >
|
||||
static inline void base64_decode_string( spb::detail::proto_field_bytes auto & output,
|
||||
istream & stream )
|
||||
{
|
||||
static constexpr uint8_t decode_table[ 256 ] = {
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57,
|
||||
58, 59, 60, 61, 128, 128, 128, 128, 128, 128, 128, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 128, 128, 128, 128, 128, 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128, 128, 128, 128
|
||||
};
|
||||
|
||||
/*static constexpr uint8_t decode_table2[] = {
|
||||
62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 128, 128, 128,
|
||||
128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 128, 128, 128, 128, 128, 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||
};*/
|
||||
|
||||
if constexpr( spb::detail::proto_field_bytes_resizable< decltype( output ) > )
|
||||
{
|
||||
output.clear( );
|
||||
}
|
||||
if( stream.current_char( ) != '"' ) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error( "expecting '\"'" );
|
||||
}
|
||||
|
||||
stream.consume_current_char( false );
|
||||
if( stream.consume( '"' ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto mask = uint8_t( 0 );
|
||||
|
||||
for( auto out_index = size_t( 0 );; )
|
||||
{
|
||||
auto view = stream.view( UINT32_MAX );
|
||||
auto length = view.find( '"' );
|
||||
auto end_found = length < view.npos;
|
||||
if( ( end_found && length % 4 != 0 ) || view.size( ) <= 4 ) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error( "invalid base64" );
|
||||
}
|
||||
length = std::min( length, view.size( ) );
|
||||
|
||||
//- align to 4 bytes
|
||||
auto aligned_length = length & ~3;
|
||||
if( aligned_length > 4 ) [[likely]]
|
||||
{
|
||||
auto out_length = ( ( aligned_length - 4 ) / 4 ) * 3;
|
||||
view = view.substr( 0, aligned_length );
|
||||
|
||||
if constexpr( spb::detail::proto_field_bytes_resizable< decltype( output ) > )
|
||||
{
|
||||
output.resize( output.size( ) + out_length );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( out_length > ( output.size( ) - out_index ) )
|
||||
{
|
||||
throw std::runtime_error( "too large base64" );
|
||||
}
|
||||
}
|
||||
|
||||
auto * p_out = output.data( ) + out_index;
|
||||
const auto * p_in = reinterpret_cast< const uint8_t * >( view.data( ) );
|
||||
const auto * p_end =
|
||||
p_in + aligned_length - 4;//- exclude the last 4 chars (possible padding)
|
||||
|
||||
while( p_in < p_end ) [[likely]]
|
||||
{
|
||||
uint8_t v0 = decode_table[ *p_in++ ];
|
||||
uint8_t v1 = decode_table[ *p_in++ ];
|
||||
uint8_t v2 = decode_table[ *p_in++ ];
|
||||
uint8_t v3 = decode_table[ *p_in++ ];
|
||||
mask |= ( v0 | v1 | v2 | v3 );
|
||||
|
||||
*p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
|
||||
*p_out++ = std::byte( ( v1 << 4 ) | ( v2 >> 2 ) );
|
||||
*p_out++ = std::byte( ( v2 << 6 ) | ( v3 ) );
|
||||
|
||||
out_index += 3;
|
||||
}
|
||||
auto consumed_bytes = p_in - reinterpret_cast< const uint8_t * >( view.data( ) );
|
||||
view.remove_prefix( consumed_bytes );
|
||||
stream.skip( consumed_bytes );
|
||||
}
|
||||
|
||||
if( end_found )
|
||||
{
|
||||
//- handle padding
|
||||
const auto * p_in = reinterpret_cast< const uint8_t * >( view.data( ) );
|
||||
|
||||
uint8_t v0 = decode_table[ *p_in++ ];
|
||||
uint8_t v1 = decode_table[ *p_in++ ];
|
||||
auto i1 = *p_in++;
|
||||
uint8_t v2 = i1 == '=' ? 0 : decode_table[ i1 ];
|
||||
auto i2 = *p_in++;
|
||||
uint8_t v3 = i2 == '=' ? 0 : decode_table[ i2 ];
|
||||
mask |= ( v0 | v1 | v2 | v3 );
|
||||
mask |= ( ( i1 == '=' ) & ( i2 != '=' ) ) ? 128 : 0;
|
||||
if( mask & 128 ) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error( "invalid base64" );
|
||||
}
|
||||
|
||||
auto padding_size = ( i1 == '=' ? 1 : 0 ) + ( i2 == '=' ? 1 : 0 );
|
||||
auto consumed_bytes = 3 - padding_size;
|
||||
//- +1 is for "
|
||||
stream.skip( 5 );
|
||||
if constexpr( spb::detail::proto_field_bytes_resizable< decltype( output ) > )
|
||||
{
|
||||
output.resize( output.size( ) + consumed_bytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( output.size( ) != out_index + consumed_bytes )
|
||||
{
|
||||
throw std::runtime_error( "too large base64" );
|
||||
}
|
||||
}
|
||||
auto * p_out = output.data( ) + out_index;
|
||||
if( padding_size == 0 )
|
||||
{
|
||||
*p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
|
||||
*p_out++ = std::byte( ( v1 << 4 ) | ( v2 >> 2 ) );
|
||||
*p_out++ = std::byte( ( v2 << 6 ) | ( v3 ) );
|
||||
}
|
||||
else if( padding_size == 1 )
|
||||
{
|
||||
*p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
|
||||
*p_out++ = std::byte( ( v1 << 4 ) | ( v2 >> 2 ) );
|
||||
}
|
||||
else if( padding_size == 2 )
|
||||
{
|
||||
*p_out++ = std::byte( ( v0 << 2 ) | ( v1 >> 4 ) );
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}// namespace spb::json::detail
|
||||
845
3rdparty/simple-protobuf/include/spb/json/deserialize.hpp
vendored
Normal file
845
3rdparty/simple-protobuf/include/spb/json/deserialize.hpp
vendored
Normal file
@ -0,0 +1,845 @@
|
||||
/***************************************************************************\
|
||||
* Name : deserialize library for json *
|
||||
* Description : all json deserialization functions *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../bits.h"
|
||||
#include "../concepts.h"
|
||||
#include "../to_from_chars.h"
|
||||
#include "../utf8.h"
|
||||
#include "base64.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <spb/io/buffer-io.hpp>
|
||||
#include <spb/io/io.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
namespace spb::json::detail
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
static const auto escape = '\\';
|
||||
|
||||
/**
|
||||
* @brief helper for std::variant visit
|
||||
* https://en.cppreference.com/w/cpp/utility/variant/visit
|
||||
*
|
||||
*/
|
||||
template < class... Ts >
|
||||
struct overloaded : Ts...
|
||||
{
|
||||
using Ts::operator( )...;
|
||||
};
|
||||
// explicit deduction guide (not needed as of C++20)
|
||||
template < class... Ts >
|
||||
overloaded( Ts... ) -> overloaded< Ts... >;
|
||||
|
||||
/**
|
||||
* @brief djb2_hash for strings
|
||||
* http://www.cse.yorku.ca/~oz/hash.html
|
||||
*
|
||||
* @param str
|
||||
* @return uint32_t
|
||||
*/
|
||||
static constexpr inline auto djb2_hash( std::string_view str ) noexcept -> uint32_t
|
||||
{
|
||||
uint32_t hash = 5381U;
|
||||
|
||||
for( auto c : str )
|
||||
{
|
||||
hash = ( ( hash << 5U ) + hash ) + uint8_t( c ); /* hash * 33 + c */
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static constexpr inline auto fnv1a_hash( std::string_view str ) noexcept -> uint64_t
|
||||
{
|
||||
uint64_t hash = 14695981039346656037ULL;
|
||||
const uint64_t prime = 1099511628211ULL;
|
||||
|
||||
for( auto c : str )
|
||||
{
|
||||
hash *= prime;
|
||||
hash ^= c;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
template < spb::detail::proto_field_bytes T >
|
||||
void clear( T & container )
|
||||
{
|
||||
if constexpr( spb::detail::proto_field_bytes_resizable< T > )
|
||||
{
|
||||
container.clear( );
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fill( container.begin( ), container.end( ), typename T::value_type( ) );
|
||||
}
|
||||
}
|
||||
|
||||
struct istream
|
||||
{
|
||||
private:
|
||||
spb::io::buffered_reader reader;
|
||||
|
||||
//- current char
|
||||
int m_current = -1;
|
||||
|
||||
std::string_view m_current_key;
|
||||
|
||||
/**
|
||||
* @brief gets the next char from the stream
|
||||
*
|
||||
* @param skip_white_space if true, skip white spaces
|
||||
*/
|
||||
void update_current( bool skip_white_space )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
auto view = reader.view( 1 );
|
||||
if( view.empty( ) )
|
||||
{
|
||||
m_current = 0;
|
||||
return;
|
||||
}
|
||||
m_current = view[ 0 ];
|
||||
if( !skip_white_space )
|
||||
{
|
||||
return;
|
||||
}
|
||||
size_t spaces = 0;
|
||||
for( auto c : view )
|
||||
{
|
||||
if( !isspace( c ) )
|
||||
{
|
||||
m_current = c;
|
||||
reader.skip( spaces );
|
||||
return;
|
||||
}
|
||||
spaces += 1;
|
||||
}
|
||||
reader.skip( spaces );
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto eof( ) -> bool
|
||||
{
|
||||
return current_char( ) == 0;
|
||||
}
|
||||
|
||||
public:
|
||||
istream( spb::io::reader reader )
|
||||
: reader( reader )
|
||||
{
|
||||
}
|
||||
|
||||
void deserialize( auto & value );
|
||||
template < size_t ordinal, typename T >
|
||||
void deserialize_variant( T & variant );
|
||||
template < typename T >
|
||||
[[nodiscard]] auto deserialize_bitfield( uint32_t bits ) -> T;
|
||||
[[nodiscard]] auto deserialize_int( ) -> int32_t;
|
||||
[[nodiscard]] auto deserialize_string_or_int( size_t min_size, size_t max_size )
|
||||
-> std::variant< std::string_view, int32_t >;
|
||||
[[nodiscard]] auto deserialize_key( size_t min_size, size_t max_size ) -> std::string_view;
|
||||
[[nodiscard]] auto current_key( ) const -> std::string_view;
|
||||
|
||||
[[nodiscard]] auto current_char( ) -> char
|
||||
{
|
||||
if( m_current < 0 )
|
||||
{
|
||||
update_current( true );
|
||||
}
|
||||
|
||||
return m_current;
|
||||
}
|
||||
/**
|
||||
* @brief consumes `current char` if its equal to c
|
||||
*
|
||||
* @param c consumed char
|
||||
* @return true if char was consumed
|
||||
*/
|
||||
[[nodiscard]] auto consume( char c ) -> bool
|
||||
{
|
||||
if( current_char( ) == c )
|
||||
{
|
||||
consume_current_char( true );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief consumes an `token`
|
||||
*
|
||||
* @param token consumed `token` (whole word)
|
||||
* @return true if `token` was consumed
|
||||
*/
|
||||
[[nodiscard]] auto consume( std::string_view token ) -> bool
|
||||
{
|
||||
assert( !token.empty( ) );
|
||||
|
||||
if( current_char( ) != token[ 0 ] )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !reader.view( token.size( ) ).starts_with( token ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto token_view = reader.view( token.size( ) + 1 ).substr( 0, token.size( ) + 1 );
|
||||
if( token_view.size( ) == token.size( ) || isspace( token_view.back( ) ) ||
|
||||
( !isalnum( token_view.back( ) ) && token_view.back( ) != '_' ) )
|
||||
{
|
||||
reader.skip( token.size( ) );
|
||||
update_current( true );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto view( size_t size ) -> std::string_view
|
||||
{
|
||||
auto result = reader.view( size );
|
||||
if( result.empty( ) )
|
||||
{
|
||||
throw std::runtime_error( "unexpected end of stream" );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void consume_current_char( bool skip_white_space ) noexcept
|
||||
{
|
||||
reader.skip( 1 );
|
||||
update_current( skip_white_space );
|
||||
}
|
||||
|
||||
void skip( size_t size )
|
||||
{
|
||||
reader.skip( size );
|
||||
m_current = -1;
|
||||
}
|
||||
void skip_value( );
|
||||
};
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_enum auto & value )
|
||||
{
|
||||
deserialize_value( stream, value );
|
||||
}
|
||||
|
||||
static inline void ignore_string( istream & stream )
|
||||
{
|
||||
if( stream.current_char( ) != '"' )
|
||||
{
|
||||
throw std::runtime_error( "expecting '\"'" );
|
||||
}
|
||||
|
||||
auto last = escape;
|
||||
for( ;; )
|
||||
{
|
||||
auto view = stream.view( UINT32_MAX );
|
||||
auto length = 0U;
|
||||
for( auto current : view )
|
||||
{
|
||||
length += 1;
|
||||
if( current == '"' && last != escape )
|
||||
{
|
||||
stream.skip( length );
|
||||
return;
|
||||
}
|
||||
//- handle \\"
|
||||
last = current != escape || last != escape ? current : ' ';
|
||||
}
|
||||
stream.skip( view.size( ) );
|
||||
}
|
||||
}
|
||||
|
||||
static inline auto deserialize_string_view( istream & stream, size_t min_size, size_t max_size )
|
||||
-> std::string_view
|
||||
{
|
||||
if( stream.current_char( ) != '"' )
|
||||
{
|
||||
throw std::runtime_error( "expecting '\"'" );
|
||||
}
|
||||
|
||||
//- +2 for '"'
|
||||
auto view = stream.view( max_size + 2 );
|
||||
auto last = escape;
|
||||
auto length = size_t( 0 );
|
||||
for( auto current : view )
|
||||
{
|
||||
length += 1;
|
||||
|
||||
if( current == '"' && last != escape )
|
||||
{
|
||||
stream.skip( length );
|
||||
|
||||
if( ( length - 2 ) >= min_size && ( length - 2 ) <= max_size )
|
||||
{
|
||||
return view.substr( 1, length - 2 );
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
//- handle \\"
|
||||
last = current != escape || last != escape ? current : ' ';
|
||||
}
|
||||
|
||||
ignore_string( stream );
|
||||
return { };
|
||||
}
|
||||
|
||||
static inline auto unicode_from_hex( istream & stream ) -> uint16_t
|
||||
{
|
||||
const auto esc_size = 4U;
|
||||
auto unicode_view = stream.view( esc_size );
|
||||
if( unicode_view.size( ) < esc_size )
|
||||
{
|
||||
throw std::runtime_error( "invalid escape sequence" );
|
||||
}
|
||||
auto value = uint16_t( 0 );
|
||||
auto result =
|
||||
spb_std_emu::from_chars( unicode_view.data( ), unicode_view.data( ) + esc_size, value, 16 );
|
||||
if( result.ec != std::errc{ } || result.ptr != unicode_view.data( ) + esc_size )
|
||||
{
|
||||
throw std::runtime_error( "invalid escape sequence" );
|
||||
}
|
||||
stream.skip( esc_size );
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline auto unescape_unicode( istream & stream, char utf8[ 4 ] ) -> uint32_t
|
||||
{
|
||||
auto value = uint32_t( unicode_from_hex( stream ) );
|
||||
if( value >= 0xD800 && value <= 0xDBFF && stream.view( 2 ).starts_with( "\\u"sv ) )
|
||||
{
|
||||
stream.skip( 2 );
|
||||
auto low = unicode_from_hex( stream );
|
||||
|
||||
if( low < 0xDC00 || low > 0xDFFF )
|
||||
{
|
||||
throw std::invalid_argument( "invalid escape sequence" );
|
||||
}
|
||||
value = ( ( value - 0xD800 ) << 10 ) + ( low - 0xDC00 ) + 0x10000;
|
||||
}
|
||||
if( auto result = spb::detail::utf8::encode_point( value, utf8 ); result != 0 )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
throw std::runtime_error( "invalid escape sequence" );
|
||||
}
|
||||
static inline auto unescape( istream & stream, char utf8[ 4 ] ) -> uint32_t
|
||||
{
|
||||
auto c = stream.current_char( );
|
||||
stream.consume_current_char( false );
|
||||
switch( c )
|
||||
{
|
||||
case '"':
|
||||
utf8[ 0 ] = '"';
|
||||
return 1;
|
||||
case '\\':
|
||||
utf8[ 0 ] = '\\';
|
||||
return 1;
|
||||
case '/':
|
||||
utf8[ 0 ] = '/';
|
||||
return 1;
|
||||
case 'b':
|
||||
utf8[ 0 ] = '\b';
|
||||
return 1;
|
||||
case 'f':
|
||||
utf8[ 0 ] = '\f';
|
||||
return 1;
|
||||
case 'n':
|
||||
utf8[ 0 ] = '\n';
|
||||
return 1;
|
||||
case 'r':
|
||||
utf8[ 0 ] = '\r';
|
||||
return 1;
|
||||
case 't':
|
||||
utf8[ 0 ] = '\t';
|
||||
return 1;
|
||||
case 'u':
|
||||
return unescape_unicode( stream, utf8 );
|
||||
default:
|
||||
throw std::runtime_error( "invalid escape sequence" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_field_string auto & value )
|
||||
{
|
||||
if( stream.current_char( ) != '"' )
|
||||
{
|
||||
throw std::runtime_error( "expecting '\"'" );
|
||||
}
|
||||
|
||||
stream.consume_current_char( false );
|
||||
|
||||
if constexpr( spb::detail::proto_field_string_resizable< decltype( value ) > )
|
||||
{
|
||||
value.clear( );
|
||||
}
|
||||
auto index = size_t( 0 );
|
||||
auto append_to_value = [ & ]( const char * str, size_t size )
|
||||
{
|
||||
if constexpr( spb::detail::proto_field_string_resizable< decltype( value ) > )
|
||||
{
|
||||
value.append( str, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( auto space_left = value.size( ) - index; size <= space_left ) [[likely]]
|
||||
{
|
||||
memcpy( value.data( ) + index, str, size );
|
||||
index += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error( "invalid string size" );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
auto view = stream.view( UINT32_MAX );
|
||||
auto found = view.find_first_of( R"("\)" );
|
||||
if( found == view.npos ) [[unlikely]]
|
||||
{
|
||||
append_to_value( view.data( ), view.size( ) );
|
||||
stream.skip( view.size( ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
append_to_value( view.data( ), found );
|
||||
// +1 for '"' or '\'
|
||||
stream.skip( found + 1 );
|
||||
if( view[ found ] == '"' ) [[likely]]
|
||||
{
|
||||
if constexpr( !spb::detail::proto_field_string_resizable< decltype( value ) > )
|
||||
{
|
||||
if( index != value.size( ) )
|
||||
{
|
||||
throw std::runtime_error( "invalid string size" );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
char utf8_buffer[ 4 ];
|
||||
auto utf8_size = unescape( stream, utf8_buffer );
|
||||
append_to_value( utf8_buffer, utf8_size );
|
||||
}
|
||||
spb::detail::utf8::validate( std::string_view( value.data( ), value.size( ) ) );
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream,
|
||||
spb::detail::proto_field_int_or_float auto & value )
|
||||
{
|
||||
if( stream.current_char( ) == '"' ) [[unlikely]]
|
||||
{
|
||||
//- https://protobuf.dev/programming-guides/proto2/#json
|
||||
//- number can be a string
|
||||
auto view = deserialize_string_view( stream, 1, UINT32_MAX );
|
||||
auto result = spb_std_emu::from_chars( view.data( ), view.data( ) + view.size( ), value );
|
||||
if( result.ec != std::errc{ } )
|
||||
{
|
||||
throw std::runtime_error( "invalid number" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto view = stream.view( UINT32_MAX );
|
||||
auto result = spb_std_emu::from_chars( view.data( ), view.data( ) + view.size( ), value );
|
||||
if( result.ec != std::errc{ } )
|
||||
{
|
||||
throw std::runtime_error( "invalid number" );
|
||||
}
|
||||
stream.skip( result.ptr - view.data( ) );
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, bool & value )
|
||||
{
|
||||
if( stream.consume( "true"sv ) )
|
||||
{
|
||||
value = true;
|
||||
}
|
||||
else if( stream.consume( "false"sv ) )
|
||||
{
|
||||
value = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error( "expecting 'true' or 'false'" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, auto & value );
|
||||
|
||||
template < typename keyT, typename valueT >
|
||||
static inline void deserialize( istream & stream, std::map< keyT, valueT > & value );
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_label_optional auto & value );
|
||||
|
||||
template < spb::detail::proto_label_repeated C >
|
||||
static inline void deserialize( istream & stream, C & value )
|
||||
{
|
||||
if( stream.consume( "null"sv ) )
|
||||
{
|
||||
value.clear( );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !stream.consume( '[' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting '['" );
|
||||
}
|
||||
|
||||
if( stream.consume( ']' ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if constexpr( std::is_same_v< typename C::value_type, bool > )
|
||||
{
|
||||
auto b = false;
|
||||
deserialize( stream, b );
|
||||
value.push_back( b );
|
||||
}
|
||||
else
|
||||
{
|
||||
deserialize( stream, value.emplace_back( ) );
|
||||
}
|
||||
} while( stream.consume( ',' ) );
|
||||
|
||||
if( !stream.consume( ']' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting ']'" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_field_bytes auto & value )
|
||||
{
|
||||
if( stream.consume( "null"sv ) )
|
||||
{
|
||||
clear( value );
|
||||
return;
|
||||
}
|
||||
|
||||
base64_decode_string( value, stream );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
void deserialize_map_key( istream & stream, T & map_key )
|
||||
{
|
||||
if constexpr( std::is_same_v< T, std::string > )
|
||||
{
|
||||
return deserialize( stream, map_key );
|
||||
}
|
||||
auto str_key_map = deserialize_string_view( stream, 1, UINT32_MAX );
|
||||
auto reader = [ ptr = str_key_map.data( ), end = str_key_map.data( ) + str_key_map.size( ) ](
|
||||
void * data, size_t size ) mutable -> size_t
|
||||
{
|
||||
size_t bytes_left = end - ptr;
|
||||
size = std::min( size, bytes_left );
|
||||
memcpy( data, ptr, size );
|
||||
ptr += size;
|
||||
return size;
|
||||
};
|
||||
auto key_stream = istream( reader );
|
||||
deserialize( key_stream, map_key );
|
||||
}
|
||||
|
||||
template < typename keyT, typename valueT >
|
||||
static inline void deserialize( istream & stream, std::map< keyT, valueT > & value )
|
||||
{
|
||||
if( stream.consume( "null"sv ) )
|
||||
{
|
||||
value.clear( );
|
||||
return;
|
||||
}
|
||||
if( !stream.consume( '{' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting '{'" );
|
||||
}
|
||||
|
||||
if( stream.consume( '}' ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
auto map_key = keyT( );
|
||||
deserialize_map_key( stream, map_key );
|
||||
if( !stream.consume( ':' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting ':'" );
|
||||
}
|
||||
auto map_value = valueT( );
|
||||
deserialize( stream, map_value );
|
||||
value.emplace( std::move( map_key ), std::move( map_value ) );
|
||||
} while( stream.consume( ',' ) );
|
||||
|
||||
if( !stream.consume( '}' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting '}'" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_label_optional auto & p_value )
|
||||
{
|
||||
if( stream.consume( "null"sv ) )
|
||||
{
|
||||
p_value.reset( );
|
||||
return;
|
||||
}
|
||||
|
||||
if( p_value.has_value( ) )
|
||||
{
|
||||
deserialize( stream, *p_value );
|
||||
}
|
||||
else
|
||||
{
|
||||
deserialize(
|
||||
stream,
|
||||
p_value.emplace( typename std::decay_t< decltype( p_value ) >::value_type( ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
static inline void deserialize( istream & stream, std::unique_ptr< T > & value )
|
||||
{
|
||||
if( stream.consume( "null"sv ) )
|
||||
{
|
||||
value.reset( );
|
||||
return;
|
||||
}
|
||||
|
||||
if( value )
|
||||
{
|
||||
deserialize( stream, *value );
|
||||
}
|
||||
else
|
||||
{
|
||||
value = std::make_unique< T >( );
|
||||
deserialize( stream, *value );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ignore_value( istream & stream );
|
||||
static inline void ignore_key_and_value( istream & stream )
|
||||
{
|
||||
ignore_string( stream );
|
||||
if( !stream.consume( ':' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting ':'" );
|
||||
}
|
||||
ignore_value( stream );
|
||||
}
|
||||
|
||||
static inline void ignore_object( istream & stream )
|
||||
{
|
||||
//- '{' was already checked by caller
|
||||
stream.consume_current_char( true );
|
||||
|
||||
if( stream.consume( '}' ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ignore_key_and_value( stream );
|
||||
} while( stream.consume( ',' ) );
|
||||
|
||||
if( !stream.consume( '}' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting '}'" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ignore_array( istream & stream )
|
||||
{
|
||||
//- '[' was already checked by caller
|
||||
stream.consume_current_char( true );
|
||||
|
||||
if( stream.consume( ']' ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ignore_value( stream );
|
||||
} while( stream.consume( ',' ) );
|
||||
|
||||
if( !stream.consume( ']' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting ']" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ignore_number( istream & stream )
|
||||
{
|
||||
auto value = double{ };
|
||||
deserialize( stream, value );
|
||||
}
|
||||
|
||||
static inline void ignore_bool( istream & stream )
|
||||
{
|
||||
auto value = bool{ };
|
||||
deserialize( stream, value );
|
||||
}
|
||||
|
||||
static inline void ignore_null( istream & stream )
|
||||
{
|
||||
if( !stream.consume( "null"sv ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting 'null'" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ignore_value( istream & stream )
|
||||
{
|
||||
switch( stream.current_char( ) )
|
||||
{
|
||||
case '{':
|
||||
return ignore_object( stream );
|
||||
case '[':
|
||||
return ignore_array( stream );
|
||||
case '"':
|
||||
return ignore_string( stream );
|
||||
case 'n':
|
||||
return ignore_null( stream );
|
||||
case 't':
|
||||
case 'f':
|
||||
return ignore_bool( stream );
|
||||
default:
|
||||
return ignore_number( stream );
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
inline auto deserialize_bitfield( istream & stream, uint32_t bits ) -> T
|
||||
{
|
||||
auto value = T( );
|
||||
deserialize( stream, value );
|
||||
spb::detail::check_if_value_fit_in_bits( value, bits );
|
||||
return value;
|
||||
}
|
||||
|
||||
template < size_t ordinal, typename T >
|
||||
static inline void deserialize_variant( istream & stream, T & variant )
|
||||
{
|
||||
deserialize( stream, variant.template emplace< ordinal >( ) );
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, auto & value )
|
||||
{
|
||||
if( !stream.consume( '{' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting '{'" );
|
||||
}
|
||||
|
||||
if( stream.consume( '}' ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
//
|
||||
//- deserialize_value is generated by the sprotoc
|
||||
//
|
||||
deserialize_value( stream, value );
|
||||
|
||||
if( stream.consume( ',' ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( stream.consume( '}' ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw std::runtime_error( "expecting '}' or ','" );
|
||||
}
|
||||
}
|
||||
|
||||
inline auto istream::deserialize_key( size_t min_size, size_t max_size ) -> std::string_view
|
||||
{
|
||||
m_current_key = deserialize_string_view( *this, min_size, max_size );
|
||||
if( !consume( ':' ) )
|
||||
{
|
||||
throw std::runtime_error( "expecting ':'" );
|
||||
}
|
||||
return m_current_key;
|
||||
}
|
||||
|
||||
inline void istream::deserialize( auto & value )
|
||||
{
|
||||
return detail::deserialize( *this, value );
|
||||
}
|
||||
|
||||
template < size_t ordinal, typename T >
|
||||
inline void istream::deserialize_variant( T & variant )
|
||||
{
|
||||
return detail::deserialize_variant< ordinal >( *this, variant );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
inline auto istream::deserialize_bitfield( uint32_t bits ) -> T
|
||||
{
|
||||
return detail::deserialize_bitfield< T >( *this, bits );
|
||||
}
|
||||
|
||||
inline auto istream::deserialize_string_or_int( size_t min_size, size_t max_size )
|
||||
-> std::variant< std::string_view, int32_t >
|
||||
{
|
||||
if( current_char( ) == '"' )
|
||||
{
|
||||
return deserialize_string_view( *this, min_size, max_size );
|
||||
}
|
||||
return deserialize_int( );
|
||||
}
|
||||
|
||||
inline auto istream::deserialize_int( ) -> int32_t
|
||||
{
|
||||
auto result = int32_t{ };
|
||||
detail::deserialize( *this, result );
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void istream::skip_value( )
|
||||
{
|
||||
return detail::ignore_value( *this );
|
||||
}
|
||||
|
||||
static inline void deserialize( auto & value, spb::io::reader reader )
|
||||
{
|
||||
auto stream = detail::istream( reader );
|
||||
return detail::deserialize( stream, value );
|
||||
}
|
||||
|
||||
}// namespace spb::json::detail
|
||||
417
3rdparty/simple-protobuf/include/spb/json/serialize.hpp
vendored
Normal file
417
3rdparty/simple-protobuf/include/spb/json/serialize.hpp
vendored
Normal file
@ -0,0 +1,417 @@
|
||||
/***************************************************************************\
|
||||
* Name : serialize library for json *
|
||||
* Description : all json serialization functions *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../concepts.h"
|
||||
|
||||
#include "../to_from_chars.h"
|
||||
#include "base64.h"
|
||||
#include "spb/json/deserialize.hpp"
|
||||
#include "spb/utf8.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <charconv>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <spb/io/io.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/types.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace spb::json::detail
|
||||
{
|
||||
struct ostream
|
||||
{
|
||||
private:
|
||||
size_t bytes_written = 0;
|
||||
spb::io::writer on_write;
|
||||
|
||||
public:
|
||||
//- flag if put ',' before value
|
||||
bool put_comma = false;
|
||||
|
||||
/**
|
||||
* @brief Construct a new ostream object
|
||||
*
|
||||
* @param writer if null, stream will skip all writes but will still count number of written
|
||||
* chars
|
||||
*/
|
||||
explicit ostream( spb::io::writer writer ) noexcept
|
||||
: on_write( writer )
|
||||
{
|
||||
}
|
||||
|
||||
void write( char c ) noexcept
|
||||
{
|
||||
if( on_write )
|
||||
{
|
||||
on_write( &c, sizeof( c ) );
|
||||
}
|
||||
|
||||
bytes_written += sizeof( c );
|
||||
}
|
||||
|
||||
void write_unicode( uint32_t codepoint )
|
||||
{
|
||||
if( codepoint <= 0xffff )
|
||||
{
|
||||
char buffer[ 8 ] = { };
|
||||
auto size = snprintf( buffer, sizeof( buffer ), "\\u%04x", codepoint );
|
||||
return write( std::string_view( buffer, size ) );
|
||||
}
|
||||
if( codepoint <= 0x10FFFF )
|
||||
{
|
||||
codepoint -= 0x10000;
|
||||
|
||||
auto high = static_cast< uint16_t >( ( codepoint >> 10 ) + 0xD800 );
|
||||
auto low = static_cast< uint16_t >( ( codepoint & 0x3FF ) + 0xDC00 );
|
||||
char buffer[ 16 ] = { };
|
||||
auto size = snprintf( buffer, sizeof( buffer ), "\\u%04x\\u%04x", high, low );
|
||||
return write( std::string_view( buffer, size ) );
|
||||
}
|
||||
throw std::invalid_argument( "invalid utf8" );
|
||||
}
|
||||
|
||||
void write( std::string_view str )
|
||||
{
|
||||
if( on_write )
|
||||
{
|
||||
on_write( str.data( ), str.size( ) );
|
||||
}
|
||||
|
||||
bytes_written += str.size( );
|
||||
}
|
||||
|
||||
void write_escaped( std::string_view str )
|
||||
{
|
||||
if( !has_escape_chars( str ) )
|
||||
{
|
||||
write( str );
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace std::literals;
|
||||
uint32_t codepoint = 0;
|
||||
uint32_t state = spb::detail::utf8::ok;
|
||||
bool decoding_utf8 = false;
|
||||
for( uint8_t c : str )
|
||||
{
|
||||
if( decoding_utf8 )
|
||||
{
|
||||
if( spb::detail::utf8::decode_point( &state, &codepoint, c ) ==
|
||||
spb::detail::utf8::ok )
|
||||
{
|
||||
write_unicode( codepoint );
|
||||
decoding_utf8 = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( is_escape( c ) )
|
||||
{
|
||||
switch( c )
|
||||
{
|
||||
case '"':
|
||||
write( R"(\")"sv );
|
||||
break;
|
||||
case '\\':
|
||||
write( R"(\\)"sv );
|
||||
break;
|
||||
case '\b':
|
||||
write( R"(\b)"sv );
|
||||
break;
|
||||
case '\f':
|
||||
write( R"(\f)"sv );
|
||||
break;
|
||||
case '\n':
|
||||
write( R"(\n)"sv );
|
||||
break;
|
||||
case '\r':
|
||||
write( R"(\r)"sv );
|
||||
break;
|
||||
case '\t':
|
||||
write( R"(\t)"sv );
|
||||
break;
|
||||
default:
|
||||
decoding_utf8 = true;
|
||||
if( spb::detail::utf8::decode_point( &state, &codepoint, c ) ==
|
||||
spb::detail::utf8::ok )
|
||||
{
|
||||
write_unicode( codepoint );
|
||||
decoding_utf8 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
write( c );
|
||||
}
|
||||
}
|
||||
if( state != spb::detail::utf8::ok )
|
||||
{
|
||||
throw std::runtime_error( "invalid utf8" );
|
||||
}
|
||||
}
|
||||
|
||||
void serialize( std::string_view key, const auto & value );
|
||||
void serialize( std::string_view value );
|
||||
|
||||
[[nodiscard]] auto size( ) const noexcept -> size_t
|
||||
{
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
private:
|
||||
static auto is_escape( uint8_t c ) -> bool
|
||||
{
|
||||
static constexpr std::string_view escape_chars = "\\\"\b\f\n\r\t<>";
|
||||
return c <= 0x1f || c >= 0x7f || escape_chars.find( c ) != std::string_view::npos;
|
||||
}
|
||||
|
||||
static auto has_escape_chars( std::string_view str ) -> bool
|
||||
{
|
||||
return std::any_of( str.begin( ), str.end( ), is_escape );
|
||||
}
|
||||
};
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
static inline void serialize_key( ostream & stream, std::string_view key )
|
||||
{
|
||||
if( stream.put_comma )
|
||||
{
|
||||
stream.write( ',' );
|
||||
}
|
||||
stream.put_comma = true;
|
||||
|
||||
if( !key.empty( ) )
|
||||
{
|
||||
stream.write( '"' );
|
||||
stream.write_escaped( key );
|
||||
stream.write( R"(":)"sv );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key, const bool & value );
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_field_int_or_float auto & value );
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_message auto & value );
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_enum auto & value );
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_field_string auto & value );
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_field_bytes auto & value );
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_label_repeated auto & value );
|
||||
template < typename keyT, typename valueT >
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const std::map< keyT, valueT > & map );
|
||||
|
||||
static inline void serialize( ostream & stream, bool value );
|
||||
static inline void serialize( ostream & stream, spb::detail::proto_field_int_or_float auto value );
|
||||
static inline void serialize( ostream & stream,
|
||||
const spb::detail::proto_field_string auto & value );
|
||||
|
||||
static inline void serialize( ostream & stream, bool value )
|
||||
{
|
||||
stream.write( value ? "true"sv : "false"sv );
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, const std::string_view & value )
|
||||
{
|
||||
stream.write( '"' );
|
||||
stream.write_escaped( value );
|
||||
stream.write( '"' );
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, const spb::detail::proto_field_string auto & value )
|
||||
{
|
||||
serialize( stream, std::string_view( value.data( ), value.size( ) ) );
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, spb::detail::proto_field_int_or_float auto value )
|
||||
{
|
||||
auto buffer = std::array< char, 32 >( );
|
||||
|
||||
auto result = spb_std_emu::to_chars( buffer.data( ), buffer.data( ) + sizeof( buffer ), value );
|
||||
stream.write(
|
||||
std::string_view( buffer.data( ), static_cast< size_t >( result.ptr - buffer.data( ) ) ) );
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key, const bool & value )
|
||||
{
|
||||
serialize_key( stream, key );
|
||||
serialize( stream, value );
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_field_int_or_float auto & value )
|
||||
{
|
||||
serialize_key( stream, key );
|
||||
serialize( stream, value );
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_field_string auto & value )
|
||||
{
|
||||
if( !value.empty( ) )
|
||||
{
|
||||
serialize_key( stream, key );
|
||||
serialize( stream, value );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_field_bytes auto & value )
|
||||
{
|
||||
if( !value.empty( ) )
|
||||
{
|
||||
serialize_key( stream, key );
|
||||
stream.write( '"' );
|
||||
base64_encode( stream, value );
|
||||
stream.write( '"' );
|
||||
}
|
||||
}
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_label_repeated auto & value )
|
||||
{
|
||||
if( value.empty( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
serialize_key( stream, key );
|
||||
stream.write( '[' );
|
||||
stream.put_comma = false;
|
||||
for( const auto & v : value )
|
||||
{
|
||||
if constexpr( std::is_same_v< typename std::decay_t< decltype( value ) >::value_type,
|
||||
bool > )
|
||||
{
|
||||
serialize( stream, { }, bool( v ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
serialize( stream, { }, v );
|
||||
}
|
||||
}
|
||||
stream.write( ']' );
|
||||
stream.put_comma = true;
|
||||
}
|
||||
|
||||
static constexpr std::string_view no_name = { };
|
||||
|
||||
template < typename keyT, typename valueT >
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const std::map< keyT, valueT > & map )
|
||||
{
|
||||
if( map.empty( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
serialize_key( stream, key );
|
||||
stream.write( '{' );
|
||||
stream.put_comma = false;
|
||||
for( auto & [ map_key, map_value ] : map )
|
||||
{
|
||||
if constexpr( std::is_same_v< keyT, std::string_view > ||
|
||||
std::is_same_v< keyT, std::string > )
|
||||
{
|
||||
serialize_key( stream, map_key );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( stream.put_comma )
|
||||
{
|
||||
stream.write( ',' );
|
||||
}
|
||||
|
||||
stream.write( '"' );
|
||||
serialize( stream, map_key );
|
||||
stream.write( R"(":)"sv );
|
||||
}
|
||||
stream.put_comma = false;
|
||||
serialize( stream, no_name, map_value );
|
||||
stream.put_comma = true;
|
||||
}
|
||||
stream.write( '}' );
|
||||
stream.put_comma = true;
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_label_optional auto & p_value )
|
||||
{
|
||||
if( p_value.has_value( ) )
|
||||
{
|
||||
return serialize( stream, key, *p_value );
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const std::unique_ptr< T > & p_value )
|
||||
{
|
||||
if( p_value )
|
||||
{
|
||||
return serialize( stream, key, *p_value );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_message auto & value )
|
||||
{
|
||||
serialize_key( stream, key );
|
||||
stream.write( '{' );
|
||||
stream.put_comma = false;
|
||||
|
||||
//
|
||||
//- serialize_value is generated by the spb-protoc
|
||||
//
|
||||
serialize_value( stream, value );
|
||||
stream.write( '}' );
|
||||
stream.put_comma = true;
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, std::string_view key,
|
||||
const spb::detail::proto_enum auto & value )
|
||||
{
|
||||
serialize_key( stream, key );
|
||||
|
||||
//
|
||||
//- serialize_value is generated by the spb-protoc
|
||||
//
|
||||
serialize_value( stream, value );
|
||||
}
|
||||
|
||||
static inline auto serialize( const auto & value, spb::io::writer on_write ) -> size_t
|
||||
{
|
||||
auto stream = ostream( on_write );
|
||||
serialize( stream, no_name, value );
|
||||
return stream.size( );
|
||||
}
|
||||
|
||||
void ostream::serialize( std::string_view key, const auto & value )
|
||||
{
|
||||
detail::serialize( *this, key, value );
|
||||
}
|
||||
|
||||
inline void ostream::serialize( std::string_view value )
|
||||
{
|
||||
detail::serialize( *this, value );
|
||||
}
|
||||
|
||||
}// namespace spb::json::detail
|
||||
208
3rdparty/simple-protobuf/include/spb/pb.hpp
vendored
Normal file
208
3rdparty/simple-protobuf/include/spb/pb.hpp
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
/***************************************************************************\
|
||||
* Name : Public API for protobuf *
|
||||
* Description : all protobuf serialize and deserialize functions *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "concepts.h"
|
||||
#include "pb/deserialize.hpp"
|
||||
#include "pb/serialize.hpp"
|
||||
#include "spb/io/io.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace spb::pb
|
||||
{
|
||||
|
||||
struct serialize_options
|
||||
{
|
||||
/**
|
||||
* @brief Writes the size of the message (as a varint) before the message itself.
|
||||
* Compatible with Google's `writeDelimitedTo` and NanoPb's PB_ENCODE_DELIMITED.
|
||||
*/
|
||||
bool delimited = false;
|
||||
};
|
||||
|
||||
struct deserialize_options
|
||||
{
|
||||
/**
|
||||
* @brief Expect the size of the message (encoded as a varint) to come before the message
|
||||
* itself. Compatible with Google's `parseDelimitedFrom` and NanoPb's PB_DECODE_DELIMITED. Will
|
||||
* return after having read the specified length; the spb::io::reader object can then be read
|
||||
* from again to get the next message (if any).
|
||||
*/
|
||||
bool delimited = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief serialize message via writer
|
||||
*
|
||||
* @param[in] message to be serialized
|
||||
* @param[in] on_write function for handling the writes
|
||||
* @param[in] options
|
||||
* @return serialized size in bytes
|
||||
* @throws exceptions only from `on_write`
|
||||
*/
|
||||
static inline auto serialize( const auto & message, spb::io::writer on_write,
|
||||
const serialize_options & options = { } ) -> size_t
|
||||
{
|
||||
auto stream = detail::ostream{ on_write };
|
||||
if( options.delimited )
|
||||
{
|
||||
detail::serialize_varint( stream, detail::serialize_size( message ) );
|
||||
}
|
||||
serialize( stream, message );
|
||||
return stream.size( );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief return protobuf serialized size in bytes
|
||||
*
|
||||
* @param[in] message to be serialized
|
||||
* @param[in] options
|
||||
* @return serialized size in bytes
|
||||
*/
|
||||
[[nodiscard]] static inline auto serialize_size( const auto & message,
|
||||
const serialize_options & options = { } ) -> size_t
|
||||
{
|
||||
return serialize( message, spb::io::writer( nullptr ), options );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief serialize message into protobuf
|
||||
*
|
||||
* @param[in] message to be serialized
|
||||
* @param[in] options
|
||||
* @param[out] result serialized protobuf
|
||||
* @return serialized size in bytes
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto serialized = std::vector< std::byte >();`
|
||||
* `spb::pb::serialize( message, serialized );`
|
||||
*/
|
||||
template < typename Message, spb::resizable_container Container >
|
||||
static inline auto serialize( const Message & message, Container & result,
|
||||
const serialize_options & options = { } ) -> size_t
|
||||
{
|
||||
const auto size = serialize_size( message, options );
|
||||
result.resize( size );
|
||||
auto writer = [ ptr = result.data( ) ]( const void * data, size_t size ) mutable
|
||||
{
|
||||
memcpy( ptr, data, size );
|
||||
ptr += size;
|
||||
};
|
||||
|
||||
serialize( message, writer, options );
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief serialize message into protobuf
|
||||
*
|
||||
* @param[in] message to be serialized
|
||||
* @param[in] options
|
||||
* @return serialized protobuf
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto serialized_message = spb::pb::serialize< std::vector< std::byte > >( message );`
|
||||
*/
|
||||
template < spb::resizable_container Container = std::string, typename Message >
|
||||
[[nodiscard]] static inline auto serialize( const Message & message,
|
||||
const serialize_options & options = { } ) -> Container
|
||||
{
|
||||
auto result = Container( );
|
||||
serialize< Message, Container >( message, result, options );
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize message from protobuf
|
||||
*
|
||||
* @param[in] reader function for handling reads
|
||||
* @param[in] options
|
||||
* @param[out] message deserialized message
|
||||
* @throws std::runtime_error on error
|
||||
*/
|
||||
static inline void deserialize( auto & message, spb::io::reader reader,
|
||||
const deserialize_options & options = { } )
|
||||
{
|
||||
detail::istream stream{ reader };
|
||||
if( options.delimited )
|
||||
{
|
||||
const auto substream_length = read_varint< uint32_t >( stream );
|
||||
auto substream = stream.sub_stream( substream_length );
|
||||
return deserialize_main( substream, message );
|
||||
}
|
||||
else
|
||||
{
|
||||
return deserialize_main( stream, message );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize message from protobuf
|
||||
*
|
||||
* @param[in] protobuf string with protobuf
|
||||
* @param[in] options
|
||||
* @param[out] message deserialized message
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto serialized = std::vector< std::byte >( ... );`
|
||||
* `auto message = Message();`
|
||||
* `spb::pb::deserialize( message, serialized );`
|
||||
*/
|
||||
template < typename Message, spb::size_container Container >
|
||||
static inline void deserialize( Message & message, const Container & protobuf,
|
||||
const deserialize_options & options = { } )
|
||||
{
|
||||
auto reader = [ ptr = protobuf.data( ), end = protobuf.data( ) + protobuf.size( ) ](
|
||||
void * data, size_t size ) mutable -> size_t
|
||||
{
|
||||
size_t bytes_left = end - ptr;
|
||||
|
||||
size = std::min( size, bytes_left );
|
||||
memcpy( data, ptr, size );
|
||||
ptr += size;
|
||||
return size;
|
||||
};
|
||||
return deserialize( message, reader, options );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize message from protobuf
|
||||
*
|
||||
* @param[in] protobuf serialized protobuf
|
||||
* @param[in] options
|
||||
* @return deserialized message
|
||||
* @throws std::runtime_error on error
|
||||
* @example `auto serialized = std::vector< std::byte >( ... );`
|
||||
* `auto message = spb::pb::deserialize< Message >( serialized );`
|
||||
*/
|
||||
template < typename Message, spb::size_container Container >
|
||||
[[nodiscard]] static inline auto deserialize( const Container & protobuf,
|
||||
const deserialize_options & options = { } ) -> Message
|
||||
{
|
||||
auto message = Message{ };
|
||||
deserialize( message, protobuf, options );
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deserialize message from reader
|
||||
*
|
||||
* @param[in] reader function for handling reads
|
||||
* @param[in] options
|
||||
* @return deserialized message
|
||||
* @throws std::runtime_error on error
|
||||
*/
|
||||
template < typename Message >
|
||||
[[nodiscard]] static inline auto deserialize( spb::io::reader reader,
|
||||
const deserialize_options & options = { } ) -> Message
|
||||
{
|
||||
auto message = Message{ };
|
||||
deserialize( message, reader, options );
|
||||
return message;
|
||||
}
|
||||
|
||||
}// namespace spb::pb
|
||||
751
3rdparty/simple-protobuf/include/spb/pb/deserialize.hpp
vendored
Normal file
751
3rdparty/simple-protobuf/include/spb/pb/deserialize.hpp
vendored
Normal file
@ -0,0 +1,751 @@
|
||||
/***************************************************************************\
|
||||
* Name : deserialize library for protobuf *
|
||||
* Description : all protobuf deserialization functions *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../bits.h"
|
||||
#include "../concepts.h"
|
||||
#include "../utf8.h"
|
||||
#include "wire-types.h"
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <spb/io/io.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace spb::pb::detail
|
||||
{
|
||||
|
||||
struct istream
|
||||
{
|
||||
private:
|
||||
spb::io::reader on_read;
|
||||
size_t m_size;
|
||||
|
||||
public:
|
||||
istream( spb::io::reader reader, size_t size = std::numeric_limits< size_t >::max( ) ) noexcept
|
||||
: on_read( reader )
|
||||
, m_size( size )
|
||||
{
|
||||
}
|
||||
|
||||
void skip( uint32_t tag );
|
||||
void read_skip( size_t size );
|
||||
|
||||
void deserialize( auto & value, uint32_t tag );
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
void deserialize_as( auto & value, uint32_t tag );
|
||||
|
||||
template < size_t ordinal, typename T >
|
||||
void deserialize_variant( T & variant, uint32_t tag );
|
||||
|
||||
template < size_t ordinal, scalar_encoder encoder, typename T >
|
||||
void deserialize_variant_as( T & variant, uint32_t tag );
|
||||
|
||||
template < scalar_encoder encoder, typename T >
|
||||
auto deserialize_bitfield_as( uint32_t bits, uint32_t tag ) -> T;
|
||||
|
||||
[[nodiscard]] auto read_byte( ) -> uint8_t
|
||||
{
|
||||
uint8_t result = { };
|
||||
read_exact( &result, sizeof( result ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto read_byte_or_eof( ) -> int
|
||||
{
|
||||
uint8_t result = { };
|
||||
if( on_read( &result, sizeof( result ) ) == 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto size( ) const -> size_t
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void read_exact( void * data, size_t size )
|
||||
{
|
||||
if( this->size( ) < size ) [[unlikely]]
|
||||
{
|
||||
throw std::runtime_error( "unexpected end of stream" );
|
||||
}
|
||||
|
||||
while( size > 0 )
|
||||
{
|
||||
auto chunk_size = on_read( data, size );
|
||||
if( chunk_size == 0 )
|
||||
{
|
||||
throw std::runtime_error( "unexpected end of stream" );
|
||||
}
|
||||
|
||||
size -= chunk_size;
|
||||
m_size -= chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto empty( ) const -> bool
|
||||
{
|
||||
return size( ) == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto sub_stream( size_t sub_size ) -> istream
|
||||
{
|
||||
if( size( ) < sub_size )
|
||||
{
|
||||
throw std::runtime_error( "unexpected end of stream" );
|
||||
}
|
||||
m_size -= sub_size;
|
||||
return istream( on_read, sub_size );
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] static inline auto wire_type_from_tag( uint32_t tag ) -> wire_type
|
||||
{
|
||||
return wire_type( tag & 0x07 );
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline auto field_from_tag( uint32_t tag ) -> uint32_t
|
||||
{
|
||||
return tag >> 3;
|
||||
}
|
||||
|
||||
static inline void check_tag( uint32_t tag )
|
||||
{
|
||||
if( field_from_tag( tag ) == 0 )
|
||||
{
|
||||
throw std::runtime_error( "invalid field id" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void check_wire_type( wire_type type1, wire_type type2 )
|
||||
{
|
||||
if( type1 != type2 )
|
||||
{
|
||||
throw std::runtime_error( "invalid wire type" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void check_if_empty( istream & stream )
|
||||
{
|
||||
if( !stream.empty( ) )
|
||||
{
|
||||
throw std::runtime_error( "unexpected data in stream" );
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline auto read_tag_or_eof( istream & stream ) -> uint32_t
|
||||
{
|
||||
auto byte_or_eof = stream.read_byte_or_eof( );
|
||||
if( byte_or_eof < 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
auto byte = uint8_t( byte_or_eof );
|
||||
auto tag = uint32_t( byte & 0x7F );
|
||||
|
||||
for( size_t shift = CHAR_BIT - 1; ( byte & 0x80 ) != 0; shift += CHAR_BIT - 1 )
|
||||
{
|
||||
if( shift >= sizeof( tag ) * CHAR_BIT )
|
||||
{
|
||||
throw std::runtime_error( "invalid tag" );
|
||||
}
|
||||
|
||||
byte = stream.read_byte( );
|
||||
tag |= uint64_t( byte & 0x7F ) << shift;
|
||||
}
|
||||
|
||||
check_tag( tag );
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
[[nodiscard]] static inline auto read_varint( istream & stream ) -> T
|
||||
{
|
||||
if constexpr( std::is_same_v< T, bool > )
|
||||
{
|
||||
switch( stream.read_byte( ) )
|
||||
{
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
return true;
|
||||
default:
|
||||
throw std::runtime_error( "invalid varint for bool" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto value = uint64_t( 0 );
|
||||
|
||||
for( auto shift = 0U; shift < sizeof( value ) * CHAR_BIT; shift += CHAR_BIT - 1 )
|
||||
{
|
||||
uint8_t byte = stream.read_byte( );
|
||||
value |= uint64_t( byte & 0x7F ) << shift;
|
||||
if( ( byte & 0x80 ) == 0 )
|
||||
{
|
||||
if constexpr( std::is_signed_v< T > && sizeof( T ) < sizeof( value ) )
|
||||
{
|
||||
//- GPB encodes signed varints always as 64-bits
|
||||
//- so int32_t(-2) is encoded as "\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01",
|
||||
// same as int64_t(-2)
|
||||
//- but it should be encoded as "\xfe\xff\xff\xff\x0f"
|
||||
value = T( value );
|
||||
}
|
||||
auto result = T( value );
|
||||
if constexpr( std::is_signed_v< T > )
|
||||
{
|
||||
if( result == std::make_signed_t< T >( value ) )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( result == value )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error( "invalid varint" );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_message auto & value,
|
||||
wire_type type );
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
static inline void deserialize_as( istream & stream,
|
||||
spb::detail::proto_field_int_or_float auto & value,
|
||||
wire_type type );
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_field_bytes auto & value,
|
||||
wire_type type );
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_label_repeated auto & value,
|
||||
wire_type type );
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_field_string auto & value,
|
||||
wire_type type );
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_repeated C >
|
||||
static inline void deserialize_as( istream & stream, C & value, wire_type type );
|
||||
|
||||
template < scalar_encoder encoder, typename keyT, typename valueT >
|
||||
static inline void deserialize_as( istream & stream, std::map< keyT, valueT > & value,
|
||||
wire_type type );
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_label_optional auto & p_value,
|
||||
wire_type type );
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_optional C >
|
||||
static inline void deserialize_as( istream & stream, C & p_value, wire_type type );
|
||||
|
||||
template < typename T >
|
||||
static inline void deserialize( istream & stream, std::unique_ptr< T > & value, wire_type type );
|
||||
|
||||
template < typename T, typename signedT, typename unsignedT >
|
||||
static auto create_tmp_var( )
|
||||
{
|
||||
if constexpr( std::is_signed< T >::value )
|
||||
{
|
||||
return signedT( );
|
||||
}
|
||||
else
|
||||
{
|
||||
return unsignedT( );
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, typename T >
|
||||
static inline auto deserialize_bitfield_as( istream & stream, uint32_t bits, wire_type type ) -> T
|
||||
{
|
||||
auto value = T( );
|
||||
if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::svarint )
|
||||
{
|
||||
check_wire_type( type, wire_type::varint );
|
||||
|
||||
auto tmp = read_varint< std::make_unsigned_t< T > >( stream );
|
||||
value = T( ( tmp >> 1 ) ^ ( ~( tmp & 1 ) + 1 ) );
|
||||
}
|
||||
else if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::varint )
|
||||
{
|
||||
check_wire_type( type, wire_type::varint );
|
||||
value = read_varint< T >( stream );
|
||||
}
|
||||
else if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::i32 )
|
||||
{
|
||||
static_assert( sizeof( T ) <= sizeof( uint32_t ) );
|
||||
|
||||
check_wire_type( type, wire_type::fixed32 );
|
||||
|
||||
if constexpr( sizeof( value ) == sizeof( uint32_t ) )
|
||||
{
|
||||
stream.read_exact( &value, sizeof( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = create_tmp_var< T, int32_t, uint32_t >( );
|
||||
stream.read_exact( &tmp, sizeof( tmp ) );
|
||||
spb::detail::check_if_value_fit_in_bits( tmp, bits );
|
||||
value = T( tmp );
|
||||
}
|
||||
}
|
||||
else if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::i64 )
|
||||
{
|
||||
static_assert( sizeof( T ) <= sizeof( uint64_t ) );
|
||||
check_wire_type( type, wire_type::fixed64 );
|
||||
|
||||
if constexpr( sizeof( value ) == sizeof( uint64_t ) )
|
||||
{
|
||||
stream.read_exact( &value, sizeof( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = create_tmp_var< T, int64_t, uint64_t >( );
|
||||
stream.read_exact( &tmp, sizeof( tmp ) );
|
||||
spb::detail::check_if_value_fit_in_bits( tmp, bits );
|
||||
value = T( tmp );
|
||||
}
|
||||
}
|
||||
spb::detail::check_if_value_fit_in_bits( value, bits );
|
||||
return value;
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
static inline void deserialize_as( istream & stream, spb::detail::proto_enum auto & value,
|
||||
wire_type type )
|
||||
{
|
||||
using T = std::remove_cvref_t< decltype( value ) >;
|
||||
using int_type = std::underlying_type_t< T >;
|
||||
|
||||
if constexpr( !scalar_encoder_is_packed( encoder ) )
|
||||
{
|
||||
check_wire_type( type, wire_type::varint );
|
||||
}
|
||||
|
||||
value = T( read_varint< int_type >( stream ) );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
static inline void deserialize_as( istream & stream,
|
||||
spb::detail::proto_field_int_or_float auto & value,
|
||||
wire_type type )
|
||||
{
|
||||
using T = std::remove_cvref_t< decltype( value ) >;
|
||||
|
||||
if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::svarint )
|
||||
{
|
||||
if constexpr( !scalar_encoder_is_packed( encoder ) )
|
||||
{
|
||||
check_wire_type( type, wire_type::varint );
|
||||
}
|
||||
auto tmp = read_varint< std::make_unsigned_t< T > >( stream );
|
||||
value = T( ( tmp >> 1 ) ^ ( ~( tmp & 1 ) + 1 ) );
|
||||
}
|
||||
else if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::varint )
|
||||
{
|
||||
if constexpr( !scalar_encoder_is_packed( encoder ) )
|
||||
{
|
||||
check_wire_type( type, wire_type::varint );
|
||||
}
|
||||
value = read_varint< T >( stream );
|
||||
}
|
||||
else if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::i32 )
|
||||
{
|
||||
static_assert( sizeof( T ) <= sizeof( uint32_t ) );
|
||||
|
||||
if constexpr( !scalar_encoder_is_packed( encoder ) )
|
||||
{
|
||||
check_wire_type( type, wire_type::fixed32 );
|
||||
}
|
||||
if constexpr( sizeof( value ) == sizeof( uint32_t ) )
|
||||
{
|
||||
stream.read_exact( &value, sizeof( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr( std::is_signed_v< T > )
|
||||
{
|
||||
auto tmp = int32_t( 0 );
|
||||
stream.read_exact( &tmp, sizeof( tmp ) );
|
||||
if( tmp > std::numeric_limits< T >::max( ) ||
|
||||
tmp < std::numeric_limits< T >::min( ) )
|
||||
{
|
||||
throw std::runtime_error( "int overflow" );
|
||||
}
|
||||
value = T( tmp );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = uint32_t( 0 );
|
||||
stream.read_exact( &tmp, sizeof( tmp ) );
|
||||
if( tmp > std::numeric_limits< T >::max( ) )
|
||||
{
|
||||
throw std::runtime_error( "int overflow" );
|
||||
}
|
||||
value = T( tmp );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr( scalar_encoder_type1( encoder ) == scalar_encoder::i64 )
|
||||
{
|
||||
static_assert( sizeof( T ) <= sizeof( uint64_t ) );
|
||||
if constexpr( !scalar_encoder_is_packed( encoder ) )
|
||||
{
|
||||
check_wire_type( type, wire_type::fixed64 );
|
||||
}
|
||||
if constexpr( sizeof( value ) == sizeof( uint64_t ) )
|
||||
{
|
||||
stream.read_exact( &value, sizeof( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr( std::is_signed_v< T > )
|
||||
{
|
||||
auto tmp = int64_t( 0 );
|
||||
stream.read_exact( &tmp, sizeof( tmp ) );
|
||||
if( tmp > std::numeric_limits< T >::max( ) ||
|
||||
tmp < std::numeric_limits< T >::min( ) )
|
||||
{
|
||||
throw std::runtime_error( "int overflow" );
|
||||
}
|
||||
value = T( tmp );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = uint64_t( 0 );
|
||||
stream.read_exact( &tmp, sizeof( tmp ) );
|
||||
if( tmp > std::numeric_limits< T >::max( ) )
|
||||
{
|
||||
throw std::runtime_error( "int overflow" );
|
||||
}
|
||||
value = T( tmp );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_label_optional auto & p_value,
|
||||
wire_type type )
|
||||
{
|
||||
auto & value = p_value.emplace( typename std::decay_t< decltype( p_value ) >::value_type( ) );
|
||||
deserialize( stream, value, type );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_optional C >
|
||||
static inline void deserialize_as( istream & stream, C & p_value, wire_type type )
|
||||
{
|
||||
auto & value = p_value.emplace( typename C::value_type( ) );
|
||||
deserialize_as< encoder >( stream, value, type );
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_field_string auto & value,
|
||||
wire_type type )
|
||||
{
|
||||
check_wire_type( type, wire_type::length_delimited );
|
||||
if constexpr( spb::detail::proto_field_string_resizable< decltype( value ) > )
|
||||
{
|
||||
value.resize( stream.size( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( value.size( ) != stream.size( ) )
|
||||
{
|
||||
throw std::runtime_error( "invalid string size" );
|
||||
}
|
||||
}
|
||||
stream.read_exact( value.data( ), stream.size( ) );
|
||||
spb::detail::utf8::validate( std::string_view( value.data( ), value.size( ) ) );
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
static inline void deserialize( istream & stream, std::unique_ptr< T > & value, wire_type type )
|
||||
{
|
||||
value = std::make_unique< T >( );
|
||||
deserialize( stream, *value, type );
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_field_bytes auto & value,
|
||||
wire_type type )
|
||||
{
|
||||
check_wire_type( type, wire_type::length_delimited );
|
||||
if constexpr( spb::detail::proto_field_bytes_resizable< decltype( value ) > )
|
||||
{
|
||||
value.resize( stream.size( ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( stream.size( ) != value.size( ) )
|
||||
{
|
||||
throw std::runtime_error( "invalid bytes size" );
|
||||
}
|
||||
}
|
||||
stream.read_exact( value.data( ), stream.size( ) );
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_label_repeated auto & value,
|
||||
wire_type type )
|
||||
{
|
||||
deserialize( stream, value.emplace_back( ), type );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_repeated C >
|
||||
static inline void deserialize_packed_as( istream & stream, C & value, wire_type type )
|
||||
{
|
||||
while( !stream.empty( ) )
|
||||
{
|
||||
if constexpr( std::is_same_v< typename C::value_type, bool > )
|
||||
{
|
||||
value.emplace_back( read_varint< bool >( stream ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
deserialize_as< encoder >( stream, value.emplace_back( ), type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_repeated C >
|
||||
static inline void deserialize_as( istream & stream, C & value, wire_type type )
|
||||
{
|
||||
if constexpr( scalar_encoder_is_packed( encoder ) )
|
||||
{
|
||||
deserialize_packed_as< encoder >( stream, value, wire_type_from_scalar_encoder( encoder ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr( std::is_same_v< typename C::value_type, bool > )
|
||||
{
|
||||
value.emplace_back( read_varint< bool >( stream ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
deserialize_as< encoder >( stream, value.emplace_back( ), type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, typename keyT, typename valueT >
|
||||
static inline void deserialize_as( istream & stream, std::map< keyT, valueT > & value,
|
||||
wire_type type )
|
||||
{
|
||||
const auto key_encoder = scalar_encoder_type1( encoder );
|
||||
const auto value_encoder = scalar_encoder_type2( encoder );
|
||||
|
||||
check_wire_type( type, wire_type::length_delimited );
|
||||
|
||||
auto pair = std::pair< keyT, valueT >( );
|
||||
auto key_defined = false;
|
||||
auto value_defined = false;
|
||||
while( !stream.empty( ) )
|
||||
{
|
||||
const auto tag = read_varint< uint32_t >( stream );
|
||||
const auto field = field_from_tag( tag );
|
||||
const auto field_type = wire_type_from_tag( tag );
|
||||
|
||||
switch( field )
|
||||
{
|
||||
case 1:
|
||||
if constexpr( std::is_integral_v< keyT > )
|
||||
{
|
||||
deserialize_as< key_encoder >( stream, pair.first, field_type );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( field_type == wire_type::length_delimited )
|
||||
{
|
||||
const auto size = read_varint< uint32_t >( stream );
|
||||
auto substream = stream.sub_stream( size );
|
||||
deserialize( substream, pair.first, field_type );
|
||||
check_if_empty( substream );
|
||||
}
|
||||
else
|
||||
{
|
||||
deserialize( stream, pair.first, field_type );
|
||||
}
|
||||
}
|
||||
key_defined = true;
|
||||
break;
|
||||
case 2:
|
||||
if constexpr( spb::detail::proto_field_number< valueT > )
|
||||
{
|
||||
deserialize_as< value_encoder >( stream, pair.second, field_type );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( field_type == wire_type::length_delimited )
|
||||
{
|
||||
const auto size = read_varint< uint32_t >( stream );
|
||||
auto substream = stream.sub_stream( size );
|
||||
deserialize( substream, pair.second, field_type );
|
||||
check_if_empty( substream );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error( "invalid field" );
|
||||
}
|
||||
}
|
||||
value_defined = true;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error( "invalid field" );
|
||||
}
|
||||
}
|
||||
if( key_defined && value_defined )
|
||||
{
|
||||
value.insert( std::move( pair ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error( "invalid map item" );
|
||||
}
|
||||
}
|
||||
|
||||
template < size_t ordinal, typename T >
|
||||
static inline void deserialize_variant( istream & stream, T & variant, wire_type type )
|
||||
{
|
||||
deserialize( stream, variant.template emplace< ordinal >( ), type );
|
||||
}
|
||||
|
||||
template < size_t ordinal, scalar_encoder encoder, typename T >
|
||||
static inline void deserialize_variant_as( istream & stream, T & variant, wire_type type )
|
||||
{
|
||||
deserialize_as< encoder >( stream, variant.template emplace< ordinal >( ), type );
|
||||
}
|
||||
|
||||
static inline void deserialize_main( istream & stream, spb::detail::proto_message auto & value )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
const auto tag = read_tag_or_eof( stream );
|
||||
if( !tag )
|
||||
{
|
||||
break;
|
||||
}
|
||||
const auto field_type = wire_type_from_tag( tag );
|
||||
|
||||
if( field_type == wire_type::length_delimited )
|
||||
{
|
||||
const auto size = read_varint< uint32_t >( stream );
|
||||
auto substream = stream.sub_stream( size );
|
||||
deserialize_value( substream, value, tag );
|
||||
check_if_empty( substream );
|
||||
}
|
||||
else
|
||||
{
|
||||
deserialize_value( stream, value, tag );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void deserialize( istream & stream, spb::detail::proto_message auto & value,
|
||||
wire_type type )
|
||||
{
|
||||
check_wire_type( type, wire_type::length_delimited );
|
||||
|
||||
while( !stream.empty( ) )
|
||||
{
|
||||
const auto tag = read_varint< uint32_t >( stream );
|
||||
const auto field_type = wire_type_from_tag( tag );
|
||||
|
||||
if( field_type == wire_type::length_delimited )
|
||||
{
|
||||
const auto size = read_varint< uint32_t >( stream );
|
||||
auto substream = stream.sub_stream( size );
|
||||
deserialize_value( substream, value, tag );
|
||||
check_if_empty( substream );
|
||||
}
|
||||
else
|
||||
{
|
||||
deserialize_value( stream, value, tag );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void istream::deserialize( auto & value, uint32_t tag )
|
||||
{
|
||||
detail::deserialize( *this, value, wire_type_from_tag( tag ) );
|
||||
}
|
||||
|
||||
inline void istream::read_skip( size_t size )
|
||||
{
|
||||
uint8_t buffer[ 64 ];
|
||||
while( size > 0 )
|
||||
{
|
||||
auto chunk_size = std::min( size, sizeof( buffer ) );
|
||||
read_exact( buffer, chunk_size );
|
||||
size -= chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
inline void istream::skip( uint32_t tag )
|
||||
{
|
||||
switch( wire_type_from_tag( tag ) )
|
||||
{
|
||||
case wire_type::varint:
|
||||
return ( void ) read_varint< uint64_t >( *this );
|
||||
case wire_type::length_delimited:
|
||||
return read_skip( size( ) );
|
||||
case wire_type::fixed32:
|
||||
return read_skip( sizeof( uint32_t ) );
|
||||
case wire_type::fixed64:
|
||||
return read_skip( sizeof( uint64_t ) );
|
||||
default:
|
||||
throw std::runtime_error( "invalid wire type" );
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
inline void istream::deserialize_as( auto & value, uint32_t tag )
|
||||
{
|
||||
detail::deserialize_as< encoder >( *this, value, wire_type_from_tag( tag ) );
|
||||
}
|
||||
|
||||
template < size_t ordinal, typename T >
|
||||
inline void istream::deserialize_variant( T & variant, uint32_t tag )
|
||||
{
|
||||
detail::deserialize_variant< ordinal >( *this, variant, wire_type_from_tag( tag ) );
|
||||
}
|
||||
|
||||
template < size_t ordinal, scalar_encoder encoder, typename T >
|
||||
inline void istream::deserialize_variant_as( T & variant, uint32_t tag )
|
||||
{
|
||||
detail::deserialize_variant_as< ordinal, encoder >( *this, variant, wire_type_from_tag( tag ) );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, typename T >
|
||||
inline auto istream::deserialize_bitfield_as( uint32_t bits, uint32_t tag ) -> T
|
||||
{
|
||||
return detail::deserialize_bitfield_as< encoder, T >( *this, bits, wire_type_from_tag( tag ) );
|
||||
}
|
||||
|
||||
static inline void deserialize( auto & value, spb::io::reader on_read )
|
||||
{
|
||||
using T = std::remove_cvref_t< decltype( value ) >;
|
||||
static_assert( spb::detail::proto_message< T > );
|
||||
|
||||
auto stream = istream( on_read );
|
||||
deserialize_main( stream, value );
|
||||
}
|
||||
|
||||
}// namespace spb::pb::detail
|
||||
386
3rdparty/simple-protobuf/include/spb/pb/serialize.hpp
vendored
Normal file
386
3rdparty/simple-protobuf/include/spb/pb/serialize.hpp
vendored
Normal file
@ -0,0 +1,386 @@
|
||||
/***************************************************************************\
|
||||
* Name : serialize library for protobuf *
|
||||
* Description : all protobuf serialization functions *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../concepts.h"
|
||||
#include "../utf8.h"
|
||||
#include "wire-types.h"
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <spb/io/io.hpp>
|
||||
#include <sys/types.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace spb::pb::detail
|
||||
{
|
||||
struct ostream
|
||||
{
|
||||
private:
|
||||
size_t bytes_written = 0;
|
||||
spb::io::writer on_write;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ostream object
|
||||
*
|
||||
* @param writer if null, stream will skip all writes but will still count number of written
|
||||
* bytes
|
||||
*/
|
||||
explicit ostream( spb::io::writer writer = nullptr ) noexcept
|
||||
: on_write( writer )
|
||||
{
|
||||
}
|
||||
|
||||
void write( const void * p_data, size_t size ) noexcept
|
||||
{
|
||||
if( on_write )
|
||||
{
|
||||
on_write( p_data, size );
|
||||
}
|
||||
|
||||
bytes_written += size;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto size( ) const noexcept -> size_t
|
||||
{
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
void serialize( uint32_t field_number, const auto & value );
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
void serialize_as( uint32_t field_number, const auto & value );
|
||||
};
|
||||
|
||||
static inline auto serialize_size( const auto & value ) -> size_t;
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
static inline void serialize_varint( ostream & stream, uint64_t value )
|
||||
{
|
||||
size_t i = 0;
|
||||
uint8_t buffer[ 10 ];
|
||||
|
||||
do
|
||||
{
|
||||
uint8_t byte = value & 0x7F;
|
||||
value >>= 7;
|
||||
byte |= value > 0 ? 0x80U : 0;
|
||||
buffer[ i++ ] = byte;
|
||||
} while( value > 0 );
|
||||
|
||||
return stream.write( buffer, i );
|
||||
}
|
||||
static inline void serialize_svarint( ostream & stream, int64_t value )
|
||||
{
|
||||
const auto tmp = uint64_t( ( value << 1 ) ^ ( value >> 63 ) );
|
||||
return serialize_varint( stream, tmp );
|
||||
}
|
||||
|
||||
static inline void serialize_tag( ostream & stream, uint32_t field_number, wire_type type )
|
||||
{
|
||||
const auto tag = ( field_number << 3 ) | uint32_t( type );
|
||||
serialize_varint( stream, tag );
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_message auto & value );
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_field_string auto & value );
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_field_bytes auto & value );
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_label_repeated auto & value );
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_repeated C >
|
||||
static inline void serialize_as( ostream & stream, uint32_t field_number, const C & value );
|
||||
|
||||
template < scalar_encoder encoder, typename keyT, typename valueT >
|
||||
static inline void serialize_as( ostream & stream, uint32_t field_number,
|
||||
const std::map< keyT, valueT > & value );
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
static inline void serialize_as( ostream & stream, uint32_t field_number,
|
||||
spb::detail::proto_field_number auto value )
|
||||
{
|
||||
serialize_tag( stream, field_number, wire_type_from_scalar_encoder( encoder ) );
|
||||
serialize_as< encoder >( stream, value );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
static inline void serialize_as( ostream & stream, const spb::detail::proto_enum auto & value )
|
||||
{
|
||||
serialize_varint( stream, int32_t( value ) );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
static inline void serialize_as( ostream & stream,
|
||||
spb::detail::proto_field_int_or_float auto value )
|
||||
{
|
||||
using T = std::remove_cvref_t< decltype( value ) >;
|
||||
|
||||
const auto type = scalar_encoder_type1( encoder );
|
||||
|
||||
if constexpr( type == scalar_encoder::varint )
|
||||
{
|
||||
static_assert( std::is_integral_v< T > );
|
||||
|
||||
if constexpr( std::is_same_v< bool, T > )
|
||||
{
|
||||
const uint8_t tmp = value ? 1 : 0;
|
||||
return stream.write( &tmp, 1 );
|
||||
}
|
||||
else if constexpr( std::is_signed_v< T > )
|
||||
{
|
||||
//- GPB is serializing all negative ints always as int64_t
|
||||
const auto u_value = uint64_t( int64_t( value ) );
|
||||
return serialize_varint( stream, u_value );
|
||||
}
|
||||
else
|
||||
{
|
||||
return serialize_varint( stream, value );
|
||||
}
|
||||
}
|
||||
else if constexpr( type == scalar_encoder::svarint )
|
||||
{
|
||||
static_assert( std::is_signed_v< T > && std::is_integral_v< T > );
|
||||
|
||||
return serialize_svarint( stream, value );
|
||||
}
|
||||
else if constexpr( type == scalar_encoder::i32 )
|
||||
{
|
||||
if constexpr( sizeof( value ) == sizeof( uint32_t ) )
|
||||
{
|
||||
return stream.write( &value, sizeof( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto tmp = uint32_t( value );
|
||||
return stream.write( &tmp, sizeof( tmp ) );
|
||||
}
|
||||
}
|
||||
else if constexpr( type == scalar_encoder::i64 )
|
||||
{
|
||||
if constexpr( sizeof( value ) == sizeof( uint64_t ) )
|
||||
{
|
||||
return stream.write( &value, sizeof( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto tmp = uint64_t( value );
|
||||
return stream.write( &tmp, sizeof( tmp ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_field_string auto & value )
|
||||
{
|
||||
if( !value.empty( ) )
|
||||
{
|
||||
spb::detail::utf8::validate( std::string_view( value.data( ), value.size( ) ) );
|
||||
serialize_tag( stream, field_number, wire_type::length_delimited );
|
||||
serialize_varint( stream, value.size( ) );
|
||||
stream.write( value.data( ), value.size( ) );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_field_bytes auto & value )
|
||||
{
|
||||
if( !value.empty( ) )
|
||||
{
|
||||
serialize_tag( stream, field_number, wire_type::length_delimited );
|
||||
serialize_varint( stream, value.size( ) );
|
||||
stream.write( value.data( ), value.size( ) );
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, typename keyT, typename valueT >
|
||||
static inline void serialize_as( ostream & stream, const std::map< keyT, valueT > & value )
|
||||
{
|
||||
const auto key_encoder = scalar_encoder_type1( encoder );
|
||||
const auto value_encoder = scalar_encoder_type2( encoder );
|
||||
|
||||
for( const auto & [ k, v ] : value )
|
||||
{
|
||||
if constexpr( std::is_integral_v< keyT > )
|
||||
{
|
||||
serialize_as< key_encoder >( stream, 1, k );
|
||||
}
|
||||
else
|
||||
{
|
||||
serialize( stream, 1, k );
|
||||
}
|
||||
if constexpr( spb::detail::proto_field_number< valueT > )
|
||||
{
|
||||
serialize_as< value_encoder >( stream, 2, v );
|
||||
}
|
||||
else
|
||||
{
|
||||
serialize( stream, 2, v );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, typename keyT, typename valueT >
|
||||
static inline void serialize_as( ostream & stream, uint32_t field_number,
|
||||
const std::map< keyT, valueT > & value )
|
||||
{
|
||||
auto size_stream = ostream( );
|
||||
serialize_as< encoder >( size_stream, value );
|
||||
const auto size = size_stream.size( );
|
||||
|
||||
serialize_tag( stream, field_number, wire_type::length_delimited );
|
||||
serialize_varint( stream, size );
|
||||
serialize_as< encoder >( stream, value );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_repeated C >
|
||||
static inline void serialize_packed_as( ostream & stream, const C & container )
|
||||
{
|
||||
for( const auto & v : container )
|
||||
{
|
||||
if constexpr( std::is_same_v< typename C::value_type, bool > )
|
||||
{
|
||||
serialize_as< encoder >( stream, bool( v ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
serialize_as< encoder >( stream, v );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_repeated C >
|
||||
static inline void serialize_as( ostream & stream, uint32_t field_number, const C & value )
|
||||
{
|
||||
if constexpr( scalar_encoder_is_packed( encoder ) )
|
||||
{
|
||||
if( value.empty( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto size_stream = ostream( );
|
||||
serialize_packed_as< encoder >( size_stream, value );
|
||||
const auto size = size_stream.size( );
|
||||
|
||||
serialize_tag( stream, field_number, wire_type::length_delimited );
|
||||
serialize_varint( stream, size );
|
||||
serialize_packed_as< encoder >( stream, value );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( const auto & v : value )
|
||||
{
|
||||
if constexpr( std::is_same_v< typename C::value_type, bool > )
|
||||
{
|
||||
serialize_as< encoder >( stream, field_number, bool( v ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
serialize_as< encoder >( stream, field_number, v );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_label_repeated auto & value )
|
||||
{
|
||||
for( const auto & v : value )
|
||||
{
|
||||
if constexpr( std::is_same_v< typename std::decay_t< decltype( value ) >::value_type,
|
||||
bool > )
|
||||
{
|
||||
serialize_as< scalar_encoder::varint >( stream, field_number, bool( v ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
serialize( stream, field_number, v );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_label_optional auto & p_value )
|
||||
{
|
||||
if( p_value.has_value( ) )
|
||||
{
|
||||
return serialize( stream, field_number, *p_value );
|
||||
}
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder, spb::detail::proto_label_optional C >
|
||||
static inline void serialize_as( ostream & stream, uint32_t field_number, const C & p_value )
|
||||
{
|
||||
if( p_value.has_value( ) )
|
||||
{
|
||||
return serialize_as< encoder >( stream, field_number, *p_value );
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const std::unique_ptr< T > & p_value )
|
||||
{
|
||||
if( p_value )
|
||||
{
|
||||
return serialize( stream, field_number, *p_value );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void serialize( ostream & stream, uint32_t field_number,
|
||||
const spb::detail::proto_message auto & value )
|
||||
{
|
||||
if( const auto size = serialize_size( value ); size > 0 )
|
||||
{
|
||||
serialize_tag( stream, field_number, wire_type::length_delimited );
|
||||
serialize_varint( stream, size );
|
||||
//
|
||||
//- serialize is generated by the spb-protoc
|
||||
//
|
||||
serialize( stream, value );
|
||||
}
|
||||
}
|
||||
|
||||
static inline auto serialize( const auto & value, spb::io::writer on_write ) -> size_t
|
||||
{
|
||||
auto stream = ostream( on_write );
|
||||
serialize( stream, value );
|
||||
return stream.size( );
|
||||
}
|
||||
|
||||
static inline auto serialize_size( const auto & value ) -> size_t
|
||||
{
|
||||
auto stream = ostream( nullptr );
|
||||
|
||||
serialize( stream, value );
|
||||
return stream.size( );
|
||||
}
|
||||
|
||||
void ostream::serialize( uint32_t field_number, const auto & value )
|
||||
{
|
||||
detail::serialize( *this, field_number, value );
|
||||
}
|
||||
|
||||
template < scalar_encoder encoder >
|
||||
void ostream::serialize_as( uint32_t field_number, const auto & value )
|
||||
{
|
||||
detail::serialize_as< encoder >( *this, field_number, value );
|
||||
}
|
||||
|
||||
}// namespace spb::pb::detail
|
||||
99
3rdparty/simple-protobuf/include/spb/pb/wire-types.h
vendored
Normal file
99
3rdparty/simple-protobuf/include/spb/pb/wire-types.h
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/***************************************************************************\
|
||||
* Name : serialize library for protobuf *
|
||||
* Description : encoding in protobuf *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace spb::pb::detail
|
||||
{
|
||||
|
||||
//- https://protobuf.dev/programming-guides/encoding/
|
||||
enum class wire_type : uint8_t
|
||||
{
|
||||
//- int32, int64, uint32, uint64, sint32, sint64, bool, enum
|
||||
varint = 0,
|
||||
//- fixed64, sfixed64, double
|
||||
fixed64 = 1,
|
||||
//- string, bytes, embedded messages, packed repeated fields
|
||||
length_delimited = 2,
|
||||
//- not used
|
||||
StartGroup = 3,
|
||||
//- not used
|
||||
EndGroup = 4,
|
||||
//- fixed32, sfixed32, float
|
||||
fixed32 = 5
|
||||
};
|
||||
|
||||
//- type1, type2 and packed flag
|
||||
enum class scalar_encoder : uint8_t
|
||||
{
|
||||
//- int32, int64, uint32, uint64, bool
|
||||
varint = 0x01,
|
||||
//- zigzag int32 or int64
|
||||
svarint = 0x02,
|
||||
//- 4 bytes
|
||||
i32 = 0x03,
|
||||
//- 8 bytes
|
||||
i64 = 0x04,
|
||||
//- packed array
|
||||
packed = 0x08
|
||||
};
|
||||
|
||||
static constexpr scalar_encoder operator&( scalar_encoder lhs, scalar_encoder rhs ) noexcept
|
||||
{
|
||||
return scalar_encoder( std::underlying_type_t< scalar_encoder >( lhs ) &
|
||||
std::underlying_type_t< scalar_encoder >( rhs ) );
|
||||
}
|
||||
|
||||
static constexpr scalar_encoder operator|( scalar_encoder lhs, scalar_encoder rhs ) noexcept
|
||||
{
|
||||
return scalar_encoder( std::underlying_type_t< scalar_encoder >( lhs ) |
|
||||
std::underlying_type_t< scalar_encoder >( rhs ) );
|
||||
}
|
||||
|
||||
static constexpr auto scalar_encoder_combine( scalar_encoder type1, scalar_encoder type2 ) noexcept
|
||||
-> scalar_encoder
|
||||
{
|
||||
return scalar_encoder( ( std::underlying_type_t< scalar_encoder >( type1 ) & 0x0f ) |
|
||||
( ( std::underlying_type_t< scalar_encoder >( type2 ) & 0x0f ) << 4 ) );
|
||||
}
|
||||
|
||||
static constexpr auto scalar_encoder_is_packed( scalar_encoder a ) noexcept -> bool
|
||||
{
|
||||
return ( a & scalar_encoder::packed ) == scalar_encoder::packed;
|
||||
}
|
||||
|
||||
static constexpr auto scalar_encoder_type1( scalar_encoder a ) noexcept -> scalar_encoder
|
||||
{
|
||||
return scalar_encoder( static_cast< std::underlying_type_t< scalar_encoder > >( a ) & 0x07 );
|
||||
}
|
||||
|
||||
static constexpr auto scalar_encoder_type2( scalar_encoder a ) noexcept -> scalar_encoder
|
||||
{
|
||||
return scalar_encoder( ( static_cast< std::underlying_type_t< scalar_encoder > >( a ) >> 4 ) &
|
||||
0x07 );
|
||||
}
|
||||
|
||||
static constexpr auto wire_type_from_scalar_encoder( scalar_encoder a ) noexcept -> wire_type
|
||||
{
|
||||
switch( scalar_encoder_type1( a ) )
|
||||
{
|
||||
case scalar_encoder::i32:
|
||||
return wire_type::fixed32;
|
||||
case scalar_encoder::i64:
|
||||
return wire_type::fixed64;
|
||||
default:
|
||||
return wire_type::varint;
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace spb::pb::detail
|
||||
217
3rdparty/simple-protobuf/include/spb/to_from_chars.h
vendored
Normal file
217
3rdparty/simple-protobuf/include/spb/to_from_chars.h
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
/***************************************************************************\
|
||||
* Name : std::to/from_chars emulation, #if __cpp_lib_to_chars < 201611L *
|
||||
* Description : string to number conversion, (std::from_chars or strtoX) *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <charconv>
|
||||
#include <string>
|
||||
|
||||
#if __cpp_lib_to_chars >= 201611L
|
||||
namespace spb_std_emu = std;
|
||||
#else
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <concepts>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
namespace spb::std_emu
|
||||
{
|
||||
struct from_chars_result
|
||||
{
|
||||
const char * ptr;
|
||||
std::errc ec;
|
||||
};
|
||||
|
||||
struct to_chars_result
|
||||
{
|
||||
const char * ptr;
|
||||
std::errc ec;
|
||||
};
|
||||
|
||||
static inline auto from_chars( const char * first, const char * last, std::integral auto & number,
|
||||
int base = 10 ) -> from_chars_result
|
||||
{
|
||||
assert( first <= last );
|
||||
char buffer[ 32 ];
|
||||
auto copy_size = std::min< size_t >( last - first, sizeof( buffer ) - 1 );
|
||||
memcpy( buffer, first, copy_size );
|
||||
buffer[ copy_size ] = '\0';
|
||||
const char * end = nullptr;
|
||||
|
||||
using T = std::decay_t< decltype( number ) >;
|
||||
|
||||
static_assert( sizeof( number ) >= 1 && sizeof( number ) <= 8, "unsupported size" );
|
||||
errno = 0;
|
||||
auto result = T( 0 );
|
||||
|
||||
if constexpr( std::is_signed_v< T > )
|
||||
{
|
||||
if constexpr( sizeof( number ) < 4 )
|
||||
{
|
||||
auto tmp = strtol( buffer, ( char ** ) &end, base );
|
||||
if( tmp <= std::numeric_limits< T >::max( ) && tmp >= std::numeric_limits< T >::min( ) )
|
||||
[[likely]]
|
||||
{
|
||||
result = T( tmp );
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
}
|
||||
else if constexpr( sizeof( number ) == 4 )
|
||||
{
|
||||
result = strtol( buffer, ( char ** ) &end, base );
|
||||
}
|
||||
else if constexpr( sizeof( number ) == 8 )
|
||||
{
|
||||
result = strtoll( buffer, ( char ** ) &end, base );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr( sizeof( number ) < 4 )
|
||||
{
|
||||
auto tmp = strtoul( buffer, ( char ** ) &end, base );
|
||||
if( tmp <= std::numeric_limits< T >::max( ) ) [[likely]]
|
||||
{
|
||||
result = T( tmp );
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
}
|
||||
else if constexpr( sizeof( number ) == 4 )
|
||||
{
|
||||
result = strtoul( buffer, ( char ** ) &end, base );
|
||||
}
|
||||
else if constexpr( sizeof( number ) == 8 )
|
||||
{
|
||||
result = strtoull( buffer, ( char ** ) &end, base );
|
||||
}
|
||||
}
|
||||
end = first + ( end - buffer );
|
||||
if( end == first )
|
||||
{
|
||||
return { first, std::errc::invalid_argument };
|
||||
}
|
||||
if( errno == ERANGE )
|
||||
{
|
||||
return { end, std::errc::result_out_of_range };
|
||||
}
|
||||
number = result;
|
||||
return { end, std::errc( ) };
|
||||
}
|
||||
|
||||
static inline auto from_chars( const char * first, const char * last,
|
||||
std::floating_point auto & number ) -> from_chars_result
|
||||
{
|
||||
assert( first <= last );
|
||||
char buffer[ 256 ];
|
||||
auto copy_size = std::min< size_t >( last - first, sizeof( buffer ) - 1 );
|
||||
memcpy( buffer, first, copy_size );
|
||||
buffer[ copy_size ] = '\0';
|
||||
const char * end = nullptr;
|
||||
|
||||
static_assert( sizeof( number ) == 4 || sizeof( number ) == 8, "unsupported size" );
|
||||
errno = 0;
|
||||
auto result = std::decay_t< decltype( number ) >{ 0 };
|
||||
if constexpr( sizeof( number ) == 4 )
|
||||
{
|
||||
result = strtof( buffer, ( char ** ) &end );
|
||||
}
|
||||
else if constexpr( sizeof( number ) == 8 )
|
||||
{
|
||||
result = strtod( buffer, ( char ** ) &end );
|
||||
}
|
||||
end = first + ( end - buffer );
|
||||
if( end == first )
|
||||
{
|
||||
return { end, std::errc::invalid_argument };
|
||||
}
|
||||
if( errno == ERANGE )
|
||||
{
|
||||
return { end, std::errc::result_out_of_range };
|
||||
}
|
||||
number = result;
|
||||
return { end, std::errc( ) };
|
||||
}
|
||||
|
||||
static inline auto to_chars( char * first, char * last, const std::integral auto & number,
|
||||
int base = 10 ) -> to_chars_result
|
||||
{
|
||||
if( base != 10 )
|
||||
{
|
||||
return { first, std::errc::invalid_argument };
|
||||
}
|
||||
|
||||
if( last <= first )
|
||||
{
|
||||
return { first, std::errc::value_too_large };
|
||||
}
|
||||
|
||||
const auto result = std::to_string( number );
|
||||
const auto buffer_size = static_cast< size_t >( last - first );
|
||||
|
||||
if( result.size( ) > buffer_size )
|
||||
{
|
||||
return { first, std::errc::value_too_large };
|
||||
}
|
||||
|
||||
memcpy( first, result.data( ), result.size( ) );
|
||||
return { first + result.size( ), std::errc{} };
|
||||
}
|
||||
|
||||
static inline auto to_chars( char * first, char * last, const std::floating_point auto & number )
|
||||
-> to_chars_result
|
||||
{
|
||||
if( last <= first )
|
||||
{
|
||||
return { first, std::errc::value_too_large };
|
||||
}
|
||||
|
||||
const auto buffer_size = last - first;
|
||||
|
||||
const char * format;
|
||||
|
||||
if constexpr( std::is_same_v< std::remove_cvref_t< decltype( number ) >, double > )
|
||||
{
|
||||
format = "%lg";
|
||||
}
|
||||
else if constexpr( std::is_same_v< std::remove_cvref_t< decltype( number ) >, long double > )
|
||||
{
|
||||
format = "%Lg";
|
||||
}
|
||||
else
|
||||
{
|
||||
format = "%g";
|
||||
}
|
||||
|
||||
const int written = snprintf( first, buffer_size, format, number );
|
||||
if( written < 0 )
|
||||
{
|
||||
return { first, std::errc::invalid_argument };
|
||||
}
|
||||
else if( written > buffer_size )
|
||||
{
|
||||
return { first, std::errc::value_too_large };
|
||||
}
|
||||
|
||||
return { first + written, std::errc{} };
|
||||
}
|
||||
|
||||
}// namespace spb::std_emu
|
||||
namespace spb_std_emu = spb::std_emu;
|
||||
#endif
|
||||
128
3rdparty/simple-protobuf/include/spb/utf8.h
vendored
Normal file
128
3rdparty/simple-protobuf/include/spb/utf8.h
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
|
||||
/***************************************************************************\
|
||||
* Name : utf8 *
|
||||
* Description : utf8 validation and utf8 to unicode convert *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* reference : https://bjoern.hoehrmann.de/utf-8/decoder/dfa/ *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
namespace spb::detail::utf8
|
||||
{
|
||||
|
||||
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||
|
||||
constexpr uint8_t ok = 0;
|
||||
|
||||
static auto inline decode_point( uint32_t * state, uint32_t * codep, uint8_t byte ) -> uint32_t
|
||||
{
|
||||
static const uint8_t utf8d[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 00..1f
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 20..3f
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 40..5f
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 60..7f
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,// 80..9f
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,// a0..bf
|
||||
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df
|
||||
0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3,// e0..ef
|
||||
0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,// f0..ff
|
||||
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1,// s0..s0
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1,// s1..s2
|
||||
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1,// s3..s4
|
||||
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1,// s5..s6
|
||||
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1,
|
||||
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,// s7..s8
|
||||
};
|
||||
|
||||
uint32_t type = utf8d[ byte ];
|
||||
|
||||
*codep = ( *state != ok ) ? ( byte & 0x3fu ) | ( *codep << 6 ) : ( 0xff >> type ) & ( byte );
|
||||
|
||||
*state = utf8d[ 256 + *state * 16 + type ];
|
||||
return *state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief encode codepoint to utf8
|
||||
*
|
||||
* @param unicode codepoint
|
||||
* @param utf8 output
|
||||
* @return size of output in bytes, 0 on error
|
||||
*/
|
||||
static inline auto encode_point( uint32_t unicode, char utf8[ 4 ] ) -> uint32_t
|
||||
{
|
||||
if( unicode <= 0x7F )
|
||||
{
|
||||
utf8[ 0 ] = ( char ) unicode;
|
||||
return 1;
|
||||
}
|
||||
if( unicode <= 0x7FF )
|
||||
{
|
||||
utf8[ 0 ] = ( char ) ( ( unicode >> 6 ) | 0xC0 );
|
||||
utf8[ 1 ] = ( char ) ( ( unicode & 0x3F ) | 0x80 );
|
||||
return 2;
|
||||
}
|
||||
if( unicode >= 0xD800 && unicode < 0xE000 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( unicode <= 0xFFFF )
|
||||
{
|
||||
utf8[ 0 ] = ( char ) ( ( unicode >> 12 ) | 0xE0 );
|
||||
utf8[ 1 ] = ( char ) ( ( ( unicode >> 6 ) & 0x3F ) | 0x80 );
|
||||
utf8[ 2 ] = ( char ) ( ( unicode & 0x3F ) | 0x80 );
|
||||
return 3;
|
||||
}
|
||||
if( unicode <= 0x10FFFF )
|
||||
{
|
||||
utf8[ 0 ] = ( char ) ( ( unicode >> 18 ) | 0xF0 );
|
||||
utf8[ 1 ] = ( char ) ( ( ( unicode >> 12 ) & 0x3F ) | 0x80 );
|
||||
utf8[ 2 ] = ( char ) ( ( ( unicode >> 6 ) & 0x3F ) | 0x80 );
|
||||
utf8[ 3 ] = ( char ) ( ( unicode & 0x3F ) | 0x80 );
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline auto is_valid( std::string_view str ) -> bool
|
||||
{
|
||||
uint32_t codepoint;
|
||||
uint32_t state = ok;
|
||||
|
||||
for( uint8_t c : str )
|
||||
{
|
||||
decode_point( &state, &codepoint, c );
|
||||
}
|
||||
|
||||
return state == ok;
|
||||
}
|
||||
|
||||
static inline void validate( std::string_view value )
|
||||
{
|
||||
if( !spb::detail::utf8::is_valid( std::string_view( value.data( ), value.size( ) ) ) )
|
||||
{
|
||||
throw std::runtime_error( "invalid utf8 string" );
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace spb::detail::utf8
|
||||
11
3rdparty/simple-protobuf/scripts/run-tests-coverage.sh
vendored
Normal file
11
3rdparty/simple-protobuf/scripts/run-tests-coverage.sh
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf coverage
|
||||
cmake -B coverage -G Ninja . -DCMAKE_BUILD_TYPE=Debug -DSPB_PROTO_BUILD_TESTS=ON -DSPB_PROTO_USE_COVERAGE=ON
|
||||
cmake --build coverage --target unit_tests
|
||||
cd coverage
|
||||
ctest --output-on-failure
|
||||
lcov --capture -o coverage.info --directory . --exclude '/usr/*' --exclude '*/test/*' -q --gcov-tool gcov
|
||||
genhtml coverage.info --output-directory report
|
||||
1
3rdparty/simple-protobuf/src/CMakeLists.txt
vendored
Normal file
1
3rdparty/simple-protobuf/src/CMakeLists.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(spb-proto-compiler)
|
||||
10
3rdparty/simple-protobuf/src/spb-proto-compiler/CMakeLists.txt
vendored
Normal file
10
3rdparty/simple-protobuf/src/spb-proto-compiler/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
project(spb-proto-compiler VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
add_executable(spb-protoc main.cpp parser/parser.cpp ast/ast-types.cpp
|
||||
ast/ast-messages-order.cpp ast/ast.cpp io/file.cpp dumper/header.cpp
|
||||
dumper/pb/dumper.cpp dumper/json/dumper.cpp dumper/dumper.cpp)
|
||||
|
||||
target_include_directories(spb-protoc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(spb-protoc PUBLIC spb-proto)
|
||||
spb_set_compile_options(spb-protoc)
|
||||
target_compile_features(spb-protoc PUBLIC cxx_std_20)
|
||||
365
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-messages-order.cpp
vendored
Normal file
365
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-messages-order.cpp
vendored
Normal file
@ -0,0 +1,365 @@
|
||||
/***************************************************************************\
|
||||
* Name : ast render *
|
||||
* Description : resolve types in ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "ast-types.h"
|
||||
#include "dumper/header.h"
|
||||
#include "proto-field.h"
|
||||
#include "proto-file.h"
|
||||
#include "proto-message.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <parser/char_stream.h>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
namespace
|
||||
{
|
||||
[[nodiscard]] auto is_imported( std::string_view full_type, const proto_files & imports ) -> bool
|
||||
{
|
||||
return std::any_of( imports.begin( ), imports.end( ),
|
||||
[ full_type ]( const auto & import ) -> bool
|
||||
{
|
||||
return full_type.size( ) > import.package.name.size( ) &&
|
||||
full_type[ import.package.name.size( ) ] == '.' &&
|
||||
full_type.starts_with( import.package.name );
|
||||
} );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_enum( std::string_view type, const proto_enums & enums ) -> bool
|
||||
{
|
||||
return std::any_of( enums.begin( ), enums.end( ), [ type ]( const auto & enum_field ) -> bool
|
||||
{ return type == enum_field.name; } );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_sub_message( std::string_view type, const proto_messages & messages ) -> bool
|
||||
{
|
||||
return std::any_of( messages.begin( ), messages.end( ),
|
||||
[ type ]( const auto & message ) -> bool { return type == message.name; } );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_resolved_sub_message( std::string_view type, const proto_messages & messages )
|
||||
-> bool
|
||||
{
|
||||
return std::any_of( messages.begin( ), messages.end( ), [ type ]( const auto & message ) -> bool
|
||||
{ return type == message.name && message.resolved > 0; } );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_sub_oneof( std::string_view type, const proto_oneofs & oneofs ) -> bool
|
||||
{
|
||||
return std::any_of( oneofs.begin( ), oneofs.end( ),
|
||||
[ type ]( const auto & oneof ) -> bool { return type == oneof.name; } );
|
||||
}
|
||||
|
||||
struct search_state
|
||||
{
|
||||
enum resolve_mode
|
||||
{
|
||||
//- only check if type is already defined
|
||||
dependencies_only,
|
||||
//- for optional fields create an std::unique_ptr< T > type and forward declare T
|
||||
//- to break cyclic dependency
|
||||
optional_pointers,
|
||||
};
|
||||
|
||||
resolve_mode mode = dependencies_only;
|
||||
size_t resolved_messages = 0;
|
||||
const proto_files & imports;
|
||||
const proto_file & file;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief search ctx to hold relation 'message -> parent_message'
|
||||
* the relation is not stored in proto_message because its not needed until now
|
||||
* and the messages get sorted (moved) later so the parent pointers will be invalid anyways
|
||||
* the parent relation is used for type dependency check and for proper order of structs
|
||||
* definition in the generated *.pb.h header file, because C++ needs proper order of type
|
||||
* dependencies. The proper order is defined by the value of `.resolved` for every message
|
||||
*
|
||||
*/
|
||||
struct search_ctx
|
||||
{
|
||||
proto_message & message;
|
||||
//- parent message (can be null for top level)
|
||||
search_ctx * p_parent;
|
||||
search_state & state;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief if the field is self-referencing and can be forward declared
|
||||
* field label must be LABEL_OPTIONAL or LABEL_PTR or LABEL_REPEATED
|
||||
*
|
||||
* @param field filed
|
||||
* @param ctx search context
|
||||
* @return true if field can be forward declared
|
||||
*/
|
||||
[[nodiscard]] auto is_self( proto_field & field, const search_ctx & ctx ) -> bool
|
||||
{
|
||||
if( field.type == proto_field::Type::MESSAGE && field.type_name == ctx.message.name )
|
||||
{
|
||||
switch( field.label )
|
||||
{
|
||||
case proto_field::Label::NONE:
|
||||
throw_parse_error( ctx.state.file, field.name,
|
||||
"Field '" + std::string( field.name ) +
|
||||
"' cannot be self-referencing (make it optional)" );
|
||||
case proto_field::Label::OPTIONAL:
|
||||
field.label = proto_field::Label::PTR;
|
||||
return true;
|
||||
case proto_field::Label::REPEATED:
|
||||
case proto_field::Label::PTR:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief if this dependency can be forward declared, do it
|
||||
* field label must be LABEL_REPEATED or LABEL_PTR
|
||||
*
|
||||
* @param field field
|
||||
* @param ctx search context
|
||||
* @return true if field can be forward declared
|
||||
*/
|
||||
[[nodiscard]] auto is_forwarded( proto_field & field, search_ctx & ctx ) -> bool
|
||||
{
|
||||
if( ctx.p_parent == nullptr )
|
||||
return false;
|
||||
|
||||
for( auto & message : ctx.p_parent->message.messages )
|
||||
{
|
||||
if( field.type_name == message.name )
|
||||
{
|
||||
switch( field.label )
|
||||
{
|
||||
case proto_field::Label::NONE:
|
||||
return false;
|
||||
case proto_field::Label::OPTIONAL:
|
||||
switch( ctx.state.mode )
|
||||
{
|
||||
case search_state::dependencies_only:
|
||||
return false;
|
||||
|
||||
case search_state::optional_pointers:
|
||||
field.label = proto_field::Label::PTR;
|
||||
//
|
||||
//- revert the mode to dependencies_only and try again
|
||||
//- reason is to create as little pointers as possible
|
||||
//
|
||||
ctx.state.mode = search_state::dependencies_only;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case proto_field::Label::REPEATED:
|
||||
case proto_field::Label::PTR:
|
||||
ctx.p_parent->message.forwards.insert( message.name );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief if this dependency references the parent message and can be forward declared
|
||||
* field label must be LABEL_OPTIONAL or LABEL_REPEATED or LABEL_PTR
|
||||
*
|
||||
* @param field field
|
||||
* @param ctx search context
|
||||
* @return true if field can be forward declared
|
||||
*/
|
||||
[[nodiscard]] auto is_parent( proto_field & field, const search_ctx & ctx ) -> bool
|
||||
{
|
||||
if( ctx.p_parent == nullptr )
|
||||
return false;
|
||||
|
||||
if( field.type_name == ctx.p_parent->message.name )
|
||||
{
|
||||
switch( field.label )
|
||||
{
|
||||
case proto_field::Label::NONE:
|
||||
throw_parse_error( ctx.state.file, field.name,
|
||||
"Field '" + std::string( field.name ) +
|
||||
"' cannot reference parent (make it optional)" );
|
||||
case proto_field::Label::OPTIONAL:
|
||||
field.label = proto_field::Label::PTR;
|
||||
return true;
|
||||
case proto_field::Label::REPEATED:
|
||||
case proto_field::Label::PTR:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_defined_in_parents( std::string_view type, const search_ctx & ctx ) -> bool
|
||||
{
|
||||
if( ctx.p_parent == nullptr )
|
||||
return false;
|
||||
|
||||
const auto & message = ctx.p_parent->message;
|
||||
if( is_enum( type, message.enums ) || is_resolved_sub_message( type, message.messages ) ||
|
||||
is_sub_oneof( type, message.oneofs ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return is_defined_in_parents( type, *ctx.p_parent );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto all_types_are_resolved( const proto_messages & messages ) -> bool
|
||||
{
|
||||
return std::all_of( messages.begin( ), messages.end( ),
|
||||
[]( const auto & message ) { return message.resolved > 0; } );
|
||||
}
|
||||
|
||||
void mark_message_as_resolved( search_ctx & ctx )
|
||||
{
|
||||
assert( ctx.message.resolved == 0 );
|
||||
|
||||
ctx.message.resolved = ++ctx.state.resolved_messages;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_message_field( search_ctx & ctx, proto_field & field ) -> bool
|
||||
{
|
||||
//- check only the first type (before .) and leave the rest for the C++ compiler to check
|
||||
//- TODO: check full type name
|
||||
const auto type_name = field.type_name.substr( 0, field.type_name.find( '.' ) );
|
||||
|
||||
return is_scalar( field.type ) || is_self( field, ctx ) ||
|
||||
is_enum( field.type_name, ctx.message.enums ) ||
|
||||
is_sub_message( type_name, ctx.message.messages ) ||
|
||||
is_sub_oneof( type_name, ctx.message.oneofs ) || is_parent( field, ctx ) ||
|
||||
is_defined_in_parents( type_name, ctx ) ||
|
||||
is_imported( field.type_name, ctx.state.imports ) || is_forwarded( field, ctx );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_field( search_ctx & ctx, const proto_field & field ) -> bool
|
||||
{
|
||||
const auto type_name = field.type_name.substr( 0, field.type_name.find( '.' ) );
|
||||
|
||||
return is_scalar( field.type ) || is_enum( field.type_name, ctx.message.enums ) ||
|
||||
is_sub_message( type_name, ctx.message.messages ) ||
|
||||
is_defined_in_parents( type_name, ctx ) ||
|
||||
is_imported( field.type_name, ctx.state.imports );
|
||||
}
|
||||
|
||||
void resolve_message_fields( search_ctx & ctx )
|
||||
{
|
||||
if( ctx.message.resolved > 0 )
|
||||
return;
|
||||
|
||||
for( auto & field : ctx.message.fields )
|
||||
{
|
||||
if( !resolve_message_field( ctx, field ) )
|
||||
return;
|
||||
}
|
||||
|
||||
for( const auto & map : ctx.message.maps )
|
||||
{
|
||||
if( !resolve_field( ctx, map.value ) )
|
||||
return;
|
||||
}
|
||||
|
||||
for( const auto & oneof : ctx.message.oneofs )
|
||||
{
|
||||
for( const auto & field : oneof.fields )
|
||||
{
|
||||
if( !resolve_field( ctx, field ) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( all_types_are_resolved( ctx.message.messages ) )
|
||||
mark_message_as_resolved( ctx );
|
||||
}
|
||||
|
||||
void resolve_message_dependencies( search_ctx & ctx )
|
||||
{
|
||||
if( ctx.message.resolved > 0 )
|
||||
return;
|
||||
|
||||
for( auto & message : ctx.message.messages )
|
||||
{
|
||||
auto sub_ctx = search_ctx{
|
||||
.message = message,
|
||||
.p_parent = &ctx,
|
||||
.state = ctx.state,
|
||||
};
|
||||
|
||||
resolve_message_dependencies( sub_ctx );
|
||||
}
|
||||
resolve_message_fields( ctx );
|
||||
}
|
||||
|
||||
[[noreturn]] void dump_unresolved_message( const proto_messages & messages,
|
||||
const proto_file & file )
|
||||
{
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
if( message.resolved == 0 )
|
||||
{
|
||||
throw_parse_error( file, message.name, "type dependency can't be resolved" );
|
||||
}
|
||||
}
|
||||
throw_parse_error( file, file.content, "type dependency can't be resolved" );
|
||||
}
|
||||
|
||||
void sort_messages( proto_messages & messages )
|
||||
{
|
||||
std::sort( messages.begin( ), messages.end( ),
|
||||
[]( const auto & a, const auto & b ) { return a.resolved < b.resolved; } );
|
||||
|
||||
for( auto & message : messages )
|
||||
{
|
||||
sort_messages( message.messages );
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
void resolve_messages_order( proto_file & file )
|
||||
{
|
||||
auto state = search_state{
|
||||
.mode = search_state::dependencies_only,
|
||||
.resolved_messages = 0,
|
||||
.imports = file.file_imports,
|
||||
.file = file,
|
||||
};
|
||||
|
||||
while( !all_types_are_resolved( file.package.messages ) )
|
||||
{
|
||||
const auto resolved_messages = state.resolved_messages;
|
||||
|
||||
auto top_level_ctx = search_ctx{
|
||||
.message = file.package,
|
||||
.p_parent = nullptr,
|
||||
.state = state,
|
||||
};
|
||||
|
||||
resolve_message_dependencies( top_level_ctx );
|
||||
|
||||
if( resolved_messages == state.resolved_messages )
|
||||
{
|
||||
if( state.mode == search_state::dependencies_only )
|
||||
{
|
||||
//- no messages were resolved using only dependencies?
|
||||
//- try to break cyclic dependency by using forward pointers declaration
|
||||
state.mode = search_state::optional_pointers;
|
||||
continue;
|
||||
}
|
||||
|
||||
dump_unresolved_message( file.package.messages, file );
|
||||
}
|
||||
}
|
||||
|
||||
sort_messages( file.package.messages );
|
||||
}
|
||||
15
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-messages-order.h
vendored
Normal file
15
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-messages-order.h
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/***************************************************************************\
|
||||
* Name : ast types resolver *
|
||||
* Description : resolve types in ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-file.h"
|
||||
|
||||
void resolve_messages_order( proto_file & file );
|
||||
419
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-types.cpp
vendored
Normal file
419
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-types.cpp
vendored
Normal file
@ -0,0 +1,419 @@
|
||||
/***************************************************************************\
|
||||
* Name : ast render *
|
||||
* Description : resolve types in ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "ast/proto-oneof.h"
|
||||
|
||||
#include "../dumper/header.h"
|
||||
#include "../parser/options.h"
|
||||
#include "proto-field.h"
|
||||
#include <algorithm>
|
||||
#include "proto-file.h"
|
||||
#include "proto-message.h"
|
||||
#include "spb/pb/wire-types.h"
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
struct search_ctx
|
||||
{
|
||||
//- `file` containing `message`
|
||||
const proto_file & file;
|
||||
//- `message` containing `field`
|
||||
const proto_message & message;
|
||||
//- parent message (null for top level)
|
||||
const search_ctx * p_parent = nullptr;
|
||||
};
|
||||
|
||||
using scalar_encoder = spb::pb::detail::scalar_encoder;
|
||||
|
||||
[[nodiscard]] auto type_parts( std::string_view type_name ) -> size_t
|
||||
{
|
||||
return size_t( std::count( type_name.cbegin( ), type_name.cend( ), '.' ) );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto is_last_part( const proto_field & field, size_t type_part ) -> bool
|
||||
{
|
||||
return type_part == type_parts( field.type_name );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_type_part( const proto_field & field, size_t type_part ) -> std::string_view
|
||||
{
|
||||
auto type_name = field.type_name;
|
||||
for( ; type_part > 0; type_part-- )
|
||||
{
|
||||
const auto dot_index = type_name.find( '.' );
|
||||
if( dot_index == type_name.npos )
|
||||
return { };
|
||||
|
||||
type_name.remove_prefix( dot_index + 1 );
|
||||
}
|
||||
|
||||
return type_name.substr( 0, type_name.find( '.' ) );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_type( const search_ctx & self, const proto_field & field,
|
||||
size_t type_part )
|
||||
-> std::pair< proto_field::Type, proto_field::BitType >;
|
||||
|
||||
[[nodiscard]] auto remove_bitfield( std::string_view type ) -> std::string_view
|
||||
{
|
||||
return type.substr( 0, type.find( ':' ) );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto convertible_types( proto_field::Type from, proto_field::BitType to ) -> bool
|
||||
{
|
||||
static constexpr auto type_map =
|
||||
std::array< std::pair< proto_field::Type, std::array< proto_field::BitType, 4 > >, 15 >{ {
|
||||
{ proto_field::Type::INT32,
|
||||
{ { proto_field::BitType::INT8, proto_field::BitType::INT16,
|
||||
proto_field::BitType::INT32 } } },
|
||||
{ proto_field::Type::INT64,
|
||||
{ { proto_field::BitType::INT8, proto_field::BitType::INT16,
|
||||
proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
|
||||
{ proto_field::Type::SINT32,
|
||||
{ { proto_field::BitType::INT8, proto_field::BitType::INT16,
|
||||
proto_field::BitType::INT32 } } },
|
||||
{ proto_field::Type::SINT64,
|
||||
{ { proto_field::BitType::INT8, proto_field::BitType::INT16,
|
||||
proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
|
||||
{ proto_field::Type::UINT32,
|
||||
{ { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
|
||||
proto_field::BitType::UINT32 } } },
|
||||
{ proto_field::Type::UINT64,
|
||||
{ { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
|
||||
proto_field::BitType::UINT32, proto_field::BitType::UINT64 } } },
|
||||
{ proto_field::Type::FIXED32,
|
||||
{ { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
|
||||
proto_field::BitType::UINT32 } } },
|
||||
{ proto_field::Type::FIXED64,
|
||||
{ { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
|
||||
proto_field::BitType::UINT32, proto_field::BitType::UINT64 } } },
|
||||
{ proto_field::Type::SFIXED32,
|
||||
{ { proto_field::BitType::INT8, proto_field::BitType::INT16,
|
||||
proto_field::BitType::INT32 } } },
|
||||
{ proto_field::Type::SFIXED64,
|
||||
{ { proto_field::BitType::INT8, proto_field::BitType::INT16,
|
||||
proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
|
||||
} };
|
||||
|
||||
for( auto [ proto_type, types ] : type_map )
|
||||
{
|
||||
if( from == proto_type )
|
||||
return std::find( types.begin( ), types.end( ), to ) != types.end( );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_scalar_bit_type( std::string_view type_name ) -> proto_field::BitType
|
||||
{
|
||||
struct type_table
|
||||
{
|
||||
std::string_view name;
|
||||
proto_field::BitType type;
|
||||
};
|
||||
|
||||
static constexpr auto scalar_types = std::array< type_table, 8 >{ {
|
||||
{ "int8"sv, proto_field::BitType::INT8 },
|
||||
{ "int16"sv, proto_field::BitType::INT16 },
|
||||
{ "int32"sv, proto_field::BitType::INT32 },
|
||||
{ "int64"sv, proto_field::BitType::INT64 },
|
||||
{ "uint8"sv, proto_field::BitType::UINT8 },
|
||||
{ "uint16"sv, proto_field::BitType::UINT16 },
|
||||
{ "uint32"sv, proto_field::BitType::UINT32 },
|
||||
{ "uint64"sv, proto_field::BitType::UINT64 },
|
||||
} };
|
||||
|
||||
for( const auto item : scalar_types )
|
||||
{
|
||||
if( item.name == type_name )
|
||||
return item.type;
|
||||
}
|
||||
|
||||
return proto_field::BitType::NONE;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_scalar_proto_type( std::string_view type_name ) -> proto_field::Type
|
||||
{
|
||||
struct type_table
|
||||
{
|
||||
std::string_view name;
|
||||
proto_field::Type type;
|
||||
};
|
||||
|
||||
static constexpr auto scalar_types = std::array< type_table, 15 >{ {
|
||||
{ "bool"sv, proto_field::Type::BOOL },
|
||||
{ "bytes"sv, proto_field::Type::BYTES },
|
||||
{ "double"sv, proto_field::Type::DOUBLE },
|
||||
{ "float"sv, proto_field::Type::FLOAT },
|
||||
{ "int32"sv, proto_field::Type::INT32 },
|
||||
{ "int64"sv, proto_field::Type::INT64 },
|
||||
{ "uint32"sv, proto_field::Type::UINT32 },
|
||||
{ "uint64"sv, proto_field::Type::UINT64 },
|
||||
{ "sint32"sv, proto_field::Type::SINT32 },
|
||||
{ "sint64"sv, proto_field::Type::SINT64 },
|
||||
{ "fixed32"sv, proto_field::Type::FIXED32 },
|
||||
{ "fixed64"sv, proto_field::Type::FIXED64 },
|
||||
{ "sfixed32"sv, proto_field::Type::SFIXED32 },
|
||||
{ "sfixed64"sv, proto_field::Type::SFIXED64 },
|
||||
{ "string"sv, proto_field::Type::STRING },
|
||||
} };
|
||||
|
||||
for( const auto item : scalar_types )
|
||||
{
|
||||
if( item.name == type_name )
|
||||
return item.type;
|
||||
}
|
||||
|
||||
return proto_field::Type::NONE;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_field_type( const proto_file & file, const proto_field & field )
|
||||
-> std::pair< proto_field::Type, proto_field::BitType >
|
||||
{
|
||||
const auto type = get_scalar_proto_type( field.type_name );
|
||||
if( type == proto_field::Type::NONE )
|
||||
return { };
|
||||
|
||||
if( const auto p_name = field.options.find( option_field_type );
|
||||
p_name != field.options.end( ) )
|
||||
{
|
||||
const auto field_type = get_scalar_bit_type( remove_bitfield( p_name->second ) );
|
||||
if( !convertible_types( type, field_type ) )
|
||||
{
|
||||
throw_parse_error( file, p_name->second,
|
||||
std::string( "incompatible int type: " ) +
|
||||
std::string( field.type_name ) + " and " +
|
||||
std::string( p_name->second ) );
|
||||
}
|
||||
return { type, field_type };
|
||||
}
|
||||
|
||||
return { type, proto_field::BitType::NONE };
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_scalar_type( const proto_file & file, const proto_field & field,
|
||||
size_t type_part )
|
||||
-> std::pair< proto_field::Type, proto_field::BitType >
|
||||
{
|
||||
if( type_part != 0 )
|
||||
return { };
|
||||
|
||||
return get_field_type( file, field );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_sub_message( const proto_message & message, const proto_field & field,
|
||||
size_t type_part ) -> const proto_message *
|
||||
{
|
||||
const auto type_name = get_type_part( field, type_part );
|
||||
const auto index = std::find_if( message.messages.begin( ), message.messages.end( ),
|
||||
[ type_name ]( const auto & sub_message ) -> bool
|
||||
{ return type_name == sub_message.name; } );
|
||||
return ( index != message.messages.end( ) ) ? &*index : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_enum( const proto_message & message, const proto_field & field,
|
||||
size_t type_part ) -> proto_field::Type
|
||||
{
|
||||
if( !is_last_part( field, type_part ) )
|
||||
return proto_field::Type::NONE;
|
||||
|
||||
const auto type_name = get_type_part( field, type_part );
|
||||
|
||||
return std::any_of( message.enums.begin( ), message.enums.end( ),
|
||||
[ type_name ]( const auto & enum_ ) -> bool
|
||||
{ return type_name == enum_.name; } )
|
||||
? proto_field::Type::ENUM
|
||||
: proto_field::Type::NONE;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_from_message( const proto_message & message, const proto_field & field,
|
||||
size_t type_part ) -> proto_field::Type
|
||||
{
|
||||
if( const auto type = resolve_enum( message, field, type_part ); type )
|
||||
return type;
|
||||
|
||||
if( const auto * sub_message = get_sub_message( message, field, type_part ); sub_message )
|
||||
{
|
||||
if( is_last_part( field, type_part ) )
|
||||
return proto_field::Type::MESSAGE;
|
||||
|
||||
return resolve_from_message( *sub_message, field, type_part + 1 );
|
||||
}
|
||||
|
||||
return proto_field::Type::NONE;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_from_parent( const search_ctx & self, const proto_field & field,
|
||||
size_t type_part ) -> proto_field::Type
|
||||
{
|
||||
if( !self.p_parent || type_part > 0 )
|
||||
return proto_field::Type::NONE;
|
||||
|
||||
return resolve_type( *self.p_parent, field, type_part ).first;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_from_import( const proto_file & import, const proto_field & field )
|
||||
-> proto_field::Type
|
||||
{
|
||||
if( field.type_name.size( ) > import.package.name.size( ) &&
|
||||
field.type_name[ import.package.name.size( ) ] == '.' &&
|
||||
field.type_name.starts_with( import.package.name ) )
|
||||
{
|
||||
return resolve_from_message( import.package, field, type_parts( import.package.name ) + 1 );
|
||||
}
|
||||
|
||||
return proto_field::Type::NONE;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto resolve_from_imports( const search_ctx & self, const proto_field & field,
|
||||
size_t type_part ) -> proto_field::Type
|
||||
{
|
||||
if( type_part > 0 )
|
||||
return proto_field::Type::NONE;
|
||||
|
||||
for( const auto & import : self.file.file_imports )
|
||||
{
|
||||
if( const auto type = resolve_from_import( import, field ); type )
|
||||
return type;
|
||||
}
|
||||
|
||||
return proto_field::Type::NONE;
|
||||
}
|
||||
|
||||
auto resolve_type( const search_ctx & self, const proto_field & field, size_t type_part )
|
||||
-> std::pair< proto_field::Type, proto_field::BitType >
|
||||
{
|
||||
if( const auto type = get_scalar_type( self.file, field, type_part ); type.first )
|
||||
return type;
|
||||
|
||||
if( const auto type = resolve_from_message( self.message, field, type_part ); type )
|
||||
return { type, proto_field::BitType::NONE };
|
||||
|
||||
if( const auto type = resolve_from_parent( self, field, type_part ); type )
|
||||
return { type, proto_field::BitType::NONE };
|
||||
|
||||
if( const auto type = resolve_from_imports( self, field, type_part ); type )
|
||||
return { type, proto_field::BitType::NONE };
|
||||
|
||||
throw_parse_error( self.file, field.type_name, "type can't be resolved" );
|
||||
}
|
||||
|
||||
void resolve_types( const search_ctx & self, proto_field & field )
|
||||
{
|
||||
const auto [ type, bit_type ] = resolve_type( self, field, 0 );
|
||||
|
||||
field.type = type;
|
||||
field.bit_type = bit_type;
|
||||
}
|
||||
|
||||
void resolve_types( const search_ctx & self, proto_map & map )
|
||||
{
|
||||
resolve_types( self, map.key );
|
||||
resolve_types( self, map.value );
|
||||
}
|
||||
|
||||
void resolve_types( const search_ctx & self, proto_oneof & oneof )
|
||||
{
|
||||
for( auto & field : oneof.fields )
|
||||
{
|
||||
resolve_types( self, field );
|
||||
}
|
||||
}
|
||||
|
||||
void resolve_types( const search_ctx & parent, proto_message & message )
|
||||
{
|
||||
auto ctx = search_ctx{
|
||||
.file = parent.file,
|
||||
.message = message,
|
||||
.p_parent = &parent,
|
||||
};
|
||||
|
||||
for( auto & field : message.fields )
|
||||
{
|
||||
resolve_types( ctx, field );
|
||||
}
|
||||
|
||||
for( auto & map : message.maps )
|
||||
{
|
||||
resolve_types( ctx, map );
|
||||
}
|
||||
|
||||
for( auto & oneof : message.oneofs )
|
||||
{
|
||||
resolve_types( ctx, oneof );
|
||||
}
|
||||
|
||||
for( auto & sub_message : message.messages )
|
||||
{
|
||||
resolve_types( ctx, sub_message );
|
||||
}
|
||||
}
|
||||
}// namespace
|
||||
|
||||
auto is_packed_array( const proto_file & file, const proto_field & field ) -> bool
|
||||
{
|
||||
if( field.label != proto_field::Label::REPEATED )
|
||||
return { };
|
||||
|
||||
if( file.syntax.version < 3 )
|
||||
{
|
||||
const auto p_packed = field.options.find( "packed" );
|
||||
return p_packed != field.options.end( ) && p_packed->second == "true";
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto p_packed = field.options.find( "packed" );
|
||||
return p_packed == field.options.end( ) || p_packed->second != "false";
|
||||
}
|
||||
}
|
||||
|
||||
auto is_scalar( const proto_field::Type & type ) -> bool
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case proto_field::Type::NONE:
|
||||
case proto_field::Type::MESSAGE:
|
||||
case proto_field::Type::ENUM:
|
||||
return false;
|
||||
|
||||
case proto_field::Type::BYTES:
|
||||
case proto_field::Type::STRING:
|
||||
case proto_field::Type::BOOL:
|
||||
case proto_field::Type::INT32:
|
||||
case proto_field::Type::UINT32:
|
||||
case proto_field::Type::INT64:
|
||||
case proto_field::Type::UINT64:
|
||||
case proto_field::Type::SINT32:
|
||||
case proto_field::Type::SINT64:
|
||||
case proto_field::Type::FLOAT:
|
||||
case proto_field::Type::FIXED32:
|
||||
case proto_field::Type::SFIXED32:
|
||||
case proto_field::Type::DOUBLE:
|
||||
case proto_field::Type::FIXED64:
|
||||
case proto_field::Type::SFIXED64:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void resolve_types( proto_file & file )
|
||||
{
|
||||
auto ctx = search_ctx{
|
||||
.file = file,
|
||||
.message = file.package,
|
||||
.p_parent = nullptr,
|
||||
};
|
||||
resolve_types( ctx, file.package );
|
||||
}
|
||||
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-types.h
vendored
Normal file
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast-types.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/***************************************************************************\
|
||||
* Name : ast types resolver *
|
||||
* Description : resolve types in ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-field.h"
|
||||
#include "proto-file.h"
|
||||
|
||||
[[nodiscard]] auto is_scalar( const proto_field::Type & type ) -> bool;
|
||||
[[nodiscard]] auto is_packed_array( const proto_file & file, const proto_field & field ) -> bool;
|
||||
|
||||
/**
|
||||
* @brief resolve types in a proto file
|
||||
*
|
||||
*/
|
||||
void resolve_types( proto_file & file );
|
||||
19
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast.cpp
vendored
Normal file
19
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast.cpp
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/***************************************************************************\
|
||||
* Name : ast render *
|
||||
* Description : resolve types in ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "ast.h"
|
||||
#include "ast-types.h"
|
||||
#include "ast-messages-order.h"
|
||||
|
||||
void resolve_messages( proto_file & file )
|
||||
{
|
||||
resolve_types( file );
|
||||
resolve_messages_order( file );
|
||||
}
|
||||
31
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast.h
vendored
Normal file
31
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/ast.h
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/***************************************************************************\
|
||||
* Name : ast render *
|
||||
* Description : resolve types in ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-field.h"
|
||||
#include "proto-file.h"
|
||||
|
||||
/**
|
||||
* @brief return scalar type or no type
|
||||
* https://protobuf.dev/programming-guides/proto3/#scalar
|
||||
*
|
||||
* @param type_name
|
||||
* @return scalar type or no type
|
||||
*/
|
||||
[[nodiscard]] auto get_encoder( const proto_field & field ) -> proto_field::Type;
|
||||
|
||||
/**
|
||||
* @brief resolve types and sort all messages in a proto file
|
||||
* so if message A depends on message B
|
||||
* then message B must be defined before message A
|
||||
*
|
||||
*/
|
||||
void resolve_messages( proto_file & file );
|
||||
20
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-comment.h
vendored
Normal file
20
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-comment.h
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for an comment *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
struct proto_comment
|
||||
{
|
||||
//- points to .proto file
|
||||
std::vector< std::string_view > comments;
|
||||
};
|
||||
47
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-common.h
vendored
Normal file
47
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-common.h
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for common definitions *
|
||||
* Link : https://protobuf.dev/programming-guides/proto3/ *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-comment.h"
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
using proto_reserved_range = std::vector< std::pair< int32_t, int32_t > >;
|
||||
using proto_reserved_name = std::unordered_set< std::string_view >;
|
||||
using proto_options = std::unordered_map< std::string_view, std::string_view >;
|
||||
|
||||
struct proto_reserved
|
||||
{
|
||||
proto_reserved_range reserved_range;
|
||||
proto_reserved_name reserved_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief base attributes for most proto types
|
||||
*
|
||||
*/
|
||||
struct proto_base
|
||||
{
|
||||
//- points to .proto file
|
||||
std::string_view name;
|
||||
|
||||
//- field number
|
||||
int32_t number;
|
||||
|
||||
proto_options options;
|
||||
proto_comment comment;
|
||||
};
|
||||
|
||||
using proto_bases = std::vector< proto_base >;
|
||||
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-enum.h
vendored
Normal file
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-enum.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for an enum *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#enum_definition *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-common.h"
|
||||
#include <vector>
|
||||
|
||||
struct proto_enum : public proto_base
|
||||
{
|
||||
proto_bases fields;
|
||||
proto_reserved reserved;
|
||||
};
|
||||
|
||||
using proto_enums = std::vector< proto_enum >;
|
||||
78
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-field.h
vendored
Normal file
78
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-field.h
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for a field *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#normal_field *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-common.h"
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <spb/pb/wire-types.h>
|
||||
|
||||
struct proto_field : public proto_base
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
NONE,
|
||||
BOOL,
|
||||
BYTES,
|
||||
DOUBLE,
|
||||
ENUM,
|
||||
FLOAT,
|
||||
FIXED32,
|
||||
FIXED64,
|
||||
INT32,
|
||||
INT64,
|
||||
MESSAGE,
|
||||
SFIXED32,
|
||||
SFIXED64,
|
||||
SINT32,
|
||||
SINT64,
|
||||
STRING,
|
||||
UINT32,
|
||||
UINT64,
|
||||
};
|
||||
|
||||
enum class BitType
|
||||
{
|
||||
NONE,
|
||||
INT8,
|
||||
INT16,
|
||||
INT32,
|
||||
INT64,
|
||||
UINT8,
|
||||
UINT16,
|
||||
UINT32,
|
||||
UINT64,
|
||||
};
|
||||
|
||||
enum class Label
|
||||
{
|
||||
//- no type modifier, only `type`
|
||||
NONE = 0,
|
||||
//- std::optional< type >
|
||||
OPTIONAL = 1,
|
||||
//- std::vector< type >
|
||||
REPEATED = 2,
|
||||
//- std::unique_ptr< type >, used to break up circular references
|
||||
PTR = 3,
|
||||
};
|
||||
|
||||
Type type = Type::NONE;
|
||||
Label label = Label::OPTIONAL;
|
||||
|
||||
//- points to .proto file
|
||||
std::string_view type_name;
|
||||
|
||||
BitType bit_type = BitType::NONE;
|
||||
std::string_view bit_field;
|
||||
};
|
||||
|
||||
using proto_fields = std::vector< proto_field >;
|
||||
40
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-file.h
vendored
Normal file
40
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-file.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for the whole proto file *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-comment.h"
|
||||
#include "proto-common.h"
|
||||
#include "proto-import.h"
|
||||
#include "proto-message.h"
|
||||
#include "proto-service.h"
|
||||
#include "proto-syntax.h"
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct proto_file;
|
||||
|
||||
using proto_files = std::vector< proto_file >;
|
||||
|
||||
struct proto_file
|
||||
{
|
||||
std::filesystem::path path;
|
||||
std::string content;
|
||||
proto_syntax syntax;
|
||||
proto_comment comment;
|
||||
proto_imports imports;
|
||||
proto_message package;
|
||||
proto_options options;
|
||||
|
||||
proto_services services;
|
||||
proto_files file_imports;
|
||||
};
|
||||
25
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-import.h
vendored
Normal file
25
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-import.h
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for imports *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#import_statement *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-comment.h"
|
||||
#include <string_view>
|
||||
|
||||
struct proto_import
|
||||
{
|
||||
//- points to .proto file
|
||||
std::string_view file_name;
|
||||
|
||||
proto_comment comments;
|
||||
};
|
||||
|
||||
using proto_imports = std::vector< proto_import >;
|
||||
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-map.h
vendored
Normal file
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-map.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for map *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#map_field *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-field.h"
|
||||
#include "proto-common.h"
|
||||
|
||||
struct proto_map : public proto_base
|
||||
{
|
||||
proto_field key;
|
||||
proto_field value;
|
||||
};
|
||||
|
||||
using proto_maps = std::vector< proto_map >;
|
||||
43
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-message.h
vendored
Normal file
43
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-message.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for message *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#message_definition *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-common.h"
|
||||
#include "proto-enum.h"
|
||||
#include "proto-field.h"
|
||||
#include "proto-map.h"
|
||||
#include "proto-oneof.h"
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
struct proto_message;
|
||||
using proto_messages = std::vector< proto_message >;
|
||||
using forwarded_declarations = std::set< std::string_view >;
|
||||
|
||||
struct proto_message : public proto_base
|
||||
{
|
||||
proto_fields fields;
|
||||
proto_reserved_range extensions;
|
||||
proto_messages messages;
|
||||
proto_maps maps;
|
||||
proto_oneofs oneofs;
|
||||
proto_enums enums;
|
||||
proto_reserved reserved;
|
||||
|
||||
//- this will be used for type dependency checking
|
||||
//- its an "resolved order" of the message in the .proto file
|
||||
//- so we can later call std::sort on `.messages`
|
||||
size_t resolved = 0;
|
||||
//- forwarded declarations needed for type dependency checking
|
||||
forwarded_declarations forwards;
|
||||
};
|
||||
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-oneof.h
vendored
Normal file
23
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-oneof.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for oneof syntax *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#oneof_and_oneof_field *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-common.h"
|
||||
#include "proto-field.h"
|
||||
#include <vector>
|
||||
|
||||
struct proto_oneof : public proto_base
|
||||
{
|
||||
proto_fields fields;
|
||||
};
|
||||
|
||||
using proto_oneofs = std::vector< proto_oneof >;
|
||||
19
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-service.h
vendored
Normal file
19
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-service.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for proto service (NOT implemented) *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#service_definition *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct proto_service
|
||||
{
|
||||
};
|
||||
|
||||
using proto_services = std::vector< proto_service >;
|
||||
21
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-syntax.h
vendored
Normal file
21
3rdparty/simple-protobuf/src/spb-proto-compiler/ast/proto-syntax.h
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/***************************************************************************\
|
||||
* Name : proto ast *
|
||||
* Description : data structure for proto syntax *
|
||||
* Link : https://protobuf.dev/reference/protobuf/proto3-spec/#syntax *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proto-comment.h"
|
||||
#include <cstdint>
|
||||
|
||||
struct proto_syntax
|
||||
{
|
||||
uint32_t version = 2;
|
||||
proto_comment comments;
|
||||
};
|
||||
42
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/dumper.cpp
vendored
Normal file
42
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/dumper.cpp
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/***************************************************************************\
|
||||
* Name : CPP dumper *
|
||||
* Description : generate C++ src files for de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dumper.h"
|
||||
#include "header.h"
|
||||
#include "pb/dumper.h"
|
||||
#include "json/dumper.h"
|
||||
|
||||
void dump_cpp_header( const proto_file & file, std::ostream & stream )
|
||||
{
|
||||
try
|
||||
{
|
||||
dump_cpp_definitions( file, stream );
|
||||
dump_json_header( file, stream );
|
||||
dump_pb_header( file, stream );
|
||||
}
|
||||
catch( const std::exception & e )
|
||||
{
|
||||
throw std::runtime_error( file.path.string( ) + ":" + e.what( ) );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_cpp( const proto_file & file, const std::filesystem::path & header_file,
|
||||
std::ostream & file_stream )
|
||||
{
|
||||
try
|
||||
{
|
||||
dump_json_cpp( file, header_file, file_stream );
|
||||
dump_pb_cpp( file, header_file, file_stream );
|
||||
}
|
||||
catch( const std::exception & e )
|
||||
{
|
||||
throw std::runtime_error( file.path.string( ) + ":" + e.what( ) );
|
||||
}
|
||||
}
|
||||
33
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/dumper.h
vendored
Normal file
33
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/dumper.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/***************************************************************************\
|
||||
* Name : CPP dumper *
|
||||
* Description : generate C++ src files for de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-file.h"
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* @brief dump C++ header file for parsed proto
|
||||
*
|
||||
* @param file parsed proto
|
||||
* @param header_file output file
|
||||
* @return C++ header file for a ast
|
||||
*/
|
||||
void dump_cpp_header( const proto_file & file, std::ostream & header_file );
|
||||
|
||||
/**
|
||||
* @brief dump C++ file for parsed proto
|
||||
*
|
||||
* @param file parsed proto
|
||||
* @param header_file generated C++ header file (ex: my.pb.h)
|
||||
* @param file_stream output file name (ex: my.pb.cpp)
|
||||
*/
|
||||
void dump_cpp( const proto_file & file, const std::filesystem::path & header_file,
|
||||
std::ostream & file_stream );
|
||||
666
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/header.cpp
vendored
Normal file
666
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/header.cpp
vendored
Normal file
@ -0,0 +1,666 @@
|
||||
/***************************************************************************\
|
||||
* Name : C++ header dumper *
|
||||
* Description : generate C++ header with all structs and enums *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "header.h"
|
||||
#include "../parser/char_stream.h"
|
||||
#include "ast/ast.h"
|
||||
#include "ast/proto-common.h"
|
||||
#include "ast/proto-field.h"
|
||||
#include "ast/proto-file.h"
|
||||
#include "ast/proto-import.h"
|
||||
#include "ast/proto-message.h"
|
||||
#include "io/file.h"
|
||||
#include "parser/options.h"
|
||||
#include "parser/parser.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <spb/json/deserialize.hpp>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using cpp_includes = std::set< std::string >;
|
||||
|
||||
void dump_comment( std::ostream & stream, const proto_comment & comment )
|
||||
{
|
||||
for( const auto & comm : comment.comments )
|
||||
{
|
||||
if( comm.starts_with( "//[[" ) )
|
||||
{
|
||||
//- ignore options in comments
|
||||
continue;
|
||||
}
|
||||
|
||||
stream << comm;
|
||||
if( comm.back( ) != '\n' )
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto trim_include( std::string_view str ) -> std::string
|
||||
{
|
||||
auto p_begin = str.data( );
|
||||
auto p_end = str.data( ) + str.size( );
|
||||
while( p_begin < p_end && isspace( *p_begin ) )
|
||||
{
|
||||
p_begin++;
|
||||
}
|
||||
|
||||
while( p_begin < p_end && isspace( p_end[ -1 ] ) )
|
||||
{
|
||||
p_end--;
|
||||
}
|
||||
|
||||
if( p_begin == p_end )
|
||||
{
|
||||
return { };
|
||||
}
|
||||
|
||||
auto add_prefix = *p_begin != '"' && *p_begin != '<';
|
||||
auto add_postfix = p_end[ -1 ] != '"' && p_end[ -1 ] != '>';
|
||||
|
||||
if( add_prefix || add_postfix )
|
||||
{
|
||||
return '"' + std::string( str ) + '"';
|
||||
}
|
||||
return std::string( str );
|
||||
}
|
||||
|
||||
void dump_includes( std::ostream & stream, const cpp_includes & includes )
|
||||
{
|
||||
for( auto & include : includes )
|
||||
{
|
||||
auto file = trim_include( include );
|
||||
if( !file.empty( ) )
|
||||
{
|
||||
stream << "#include " << file << "\n";
|
||||
}
|
||||
}
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
auto contains_map( const proto_messages & messages ) -> bool
|
||||
{
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
if( !message.maps.empty( ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( contains_map( message.messages ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto contains_oneof( const proto_messages & messages ) -> bool
|
||||
{
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
if( !message.oneofs.empty( ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( contains_oneof( message.messages ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void get_std_includes( cpp_includes & includes, const proto_file & file )
|
||||
{
|
||||
includes.insert( "<spb/json.hpp>" );
|
||||
includes.insert( "<spb/pb.hpp>" );
|
||||
includes.insert( "<cstdint>" );
|
||||
includes.insert( "<cstddef>" );
|
||||
|
||||
if( contains_map( file.package.messages ) )
|
||||
{
|
||||
includes.insert( "<map>" );
|
||||
}
|
||||
if( contains_oneof( file.package.messages ) )
|
||||
{
|
||||
includes.insert( "<variant>" );
|
||||
}
|
||||
}
|
||||
|
||||
void get_include_from_options( cpp_includes & includes, const proto_options & options,
|
||||
const proto_options & message_options,
|
||||
const proto_options & file_options, std::string_view option_include )
|
||||
{
|
||||
if( auto include = options.find( option_include ); include != options.end( ) )
|
||||
{
|
||||
includes.insert( std::string( include->second ) );
|
||||
return;
|
||||
}
|
||||
if( auto include = message_options.find( option_include ); include != message_options.end( ) )
|
||||
{
|
||||
includes.insert( std::string( include->second ) );
|
||||
return;
|
||||
}
|
||||
if( auto include = file_options.find( option_include ); include != file_options.end( ) )
|
||||
{
|
||||
includes.insert( std::string( include->second ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void get_includes_from_field( cpp_includes & includes, const proto_field & field,
|
||||
const proto_message & message, const proto_file & file )
|
||||
{
|
||||
if( field.label == proto_field::Label::OPTIONAL )
|
||||
{
|
||||
get_include_from_options( includes, field.options, message.options, file.options,
|
||||
option_optional_include );
|
||||
}
|
||||
if( field.label == proto_field::Label::REPEATED )
|
||||
{
|
||||
get_include_from_options( includes, field.options, message.options, file.options,
|
||||
option_repeated_include );
|
||||
}
|
||||
if( field.label == proto_field::Label::PTR )
|
||||
{
|
||||
get_include_from_options( includes, field.options, message.options, file.options,
|
||||
option_pointer_include );
|
||||
}
|
||||
if( field.type == proto_field::Type::STRING )
|
||||
{
|
||||
get_include_from_options( includes, field.options, message.options, file.options,
|
||||
option_string_include );
|
||||
}
|
||||
if( field.type == proto_field::Type::BYTES )
|
||||
{
|
||||
get_include_from_options( includes, field.options, message.options, file.options,
|
||||
option_bytes_include );
|
||||
}
|
||||
}
|
||||
|
||||
void get_message_includes( cpp_includes & includes, const proto_message & message,
|
||||
const proto_file & file )
|
||||
{
|
||||
for( const auto & field : message.fields )
|
||||
{
|
||||
get_includes_from_field( includes, field, message, file );
|
||||
}
|
||||
|
||||
for( const auto & oneof : message.oneofs )
|
||||
{
|
||||
for( const auto & field : oneof.fields )
|
||||
{
|
||||
get_includes_from_field( includes, field, message, file );
|
||||
}
|
||||
}
|
||||
|
||||
for( const auto & map : message.maps )
|
||||
{
|
||||
get_includes_from_field( includes, map.key, message, file );
|
||||
get_includes_from_field( includes, map.value, message, file );
|
||||
}
|
||||
|
||||
for( const auto & m : message.messages )
|
||||
{
|
||||
get_message_includes( includes, m, file );
|
||||
}
|
||||
}
|
||||
|
||||
void get_user_includes( cpp_includes & includes, const proto_file & file )
|
||||
{
|
||||
get_message_includes( includes, file.package, file );
|
||||
}
|
||||
|
||||
void get_imports( cpp_includes & includes, const proto_file & file )
|
||||
{
|
||||
for( const auto & import : file.file_imports )
|
||||
{
|
||||
includes.insert( "\"" + cpp_file_name_from_proto( import.path, ".pb.h" ).string( ) + "\"" );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_pragma( std::ostream & stream, const proto_file & )
|
||||
{
|
||||
stream << "#pragma once\n\n";
|
||||
}
|
||||
|
||||
void dump_syntax( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
dump_comment( stream, file.syntax.comments );
|
||||
}
|
||||
|
||||
auto type_literal_suffix( proto_field::Type type ) -> std::string_view
|
||||
{
|
||||
static constexpr auto type_map =
|
||||
std::array< std::pair< proto_field::Type, std::string_view >, 12 >{ {
|
||||
{ proto_field::Type::INT64, "LL" },
|
||||
{ proto_field::Type::UINT32, "U" },
|
||||
{ proto_field::Type::UINT64, "ULL" },
|
||||
{ proto_field::Type::SINT64, "LL" },
|
||||
{ proto_field::Type::FIXED32, "U" },
|
||||
{ proto_field::Type::FIXED64, "ULL" },
|
||||
{ proto_field::Type::SFIXED64, "LL" },
|
||||
{ proto_field::Type::FLOAT, "F" },
|
||||
} };
|
||||
|
||||
for( auto [ proto_type, suffix ] : type_map )
|
||||
{
|
||||
if( type == proto_type )
|
||||
{
|
||||
return suffix;
|
||||
}
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
auto get_field_bits( const proto_field & field ) -> std::string_view
|
||||
{
|
||||
if( auto p_name = field.options.find( option_field_type ); p_name != field.options.end( ) )
|
||||
{
|
||||
auto bitfield = p_name->second;
|
||||
if( auto index = bitfield.find( ':' ); index != std::string_view::npos )
|
||||
{
|
||||
const_cast< proto_field & >( field ).bit_field = bitfield.substr( index + 1 );
|
||||
return bitfield.substr( index );
|
||||
}
|
||||
}
|
||||
return { };
|
||||
}
|
||||
|
||||
auto get_container_type( const proto_options & options, const proto_options & message_options,
|
||||
const proto_options & file_options, std::string_view option,
|
||||
std::string_view ctype, std::string_view default_type = { } )
|
||||
-> std::string
|
||||
{
|
||||
if( auto p_name = options.find( option ); p_name != options.end( ) )
|
||||
{
|
||||
return replace( p_name->second, "$", ctype );
|
||||
}
|
||||
|
||||
if( auto p_name = message_options.find( option ); p_name != message_options.end( ) )
|
||||
{
|
||||
return replace( p_name->second, "$", ctype );
|
||||
}
|
||||
|
||||
if( auto p_name = file_options.find( option ); p_name != file_options.end( ) )
|
||||
{
|
||||
return replace( p_name->second, "$", ctype );
|
||||
}
|
||||
|
||||
return replace( default_type, "$", ctype );
|
||||
}
|
||||
|
||||
auto get_enum_type( const proto_file & file, const proto_options & options,
|
||||
const proto_options & message_options, const proto_options & file_options,
|
||||
std::string_view default_type ) -> std::string_view
|
||||
{
|
||||
static constexpr auto type_map =
|
||||
std::array< std::pair< std::string_view, std::string_view >, 6 >{ {
|
||||
{ "int8"sv, "int8_t"sv },
|
||||
{ "uint8"sv, "uint8_t"sv },
|
||||
{ "int16"sv, "int16_t"sv },
|
||||
{ "uint16"sv, "uint16_t"sv },
|
||||
{ "int32"sv, "int32_t"sv },
|
||||
} };
|
||||
|
||||
auto ctype_from_pb = [ & ]( std::string_view type )
|
||||
{
|
||||
for( auto [ proto_type, c_type ] : type_map )
|
||||
{
|
||||
if( type == proto_type )
|
||||
{
|
||||
return c_type;
|
||||
}
|
||||
}
|
||||
throw_parse_error( file, type, "invalid enum type: " + std::string( type ) );
|
||||
};
|
||||
|
||||
if( auto p_name = options.find( option_enum_type ); p_name != options.end( ) )
|
||||
{
|
||||
return ctype_from_pb( p_name->second );
|
||||
}
|
||||
|
||||
if( auto p_name = message_options.find( option_enum_type ); p_name != message_options.end( ) )
|
||||
{
|
||||
return ctype_from_pb( p_name->second );
|
||||
}
|
||||
|
||||
if( auto p_name = file_options.find( option_enum_type ); p_name != file_options.end( ) )
|
||||
{
|
||||
return ctype_from_pb( p_name->second );
|
||||
}
|
||||
|
||||
return default_type;
|
||||
}
|
||||
|
||||
auto convert_to_ctype( const proto_file & file, const proto_field & field,
|
||||
const proto_message & message = { } ) -> std::string
|
||||
{
|
||||
if( field.bit_type != proto_field::BitType::NONE )
|
||||
{
|
||||
switch( field.bit_type )
|
||||
{
|
||||
case proto_field::BitType::NONE:
|
||||
throw_parse_error( file, field.type_name, "invalid type" );
|
||||
case proto_field::BitType::INT8:
|
||||
return "int8_t";
|
||||
case proto_field::BitType::INT16:
|
||||
return "int16_t";
|
||||
case proto_field::BitType::INT32:
|
||||
return "int32_t";
|
||||
case proto_field::BitType::INT64:
|
||||
return "int64_t";
|
||||
case proto_field::BitType::UINT8:
|
||||
return "uint8_t";
|
||||
case proto_field::BitType::UINT16:
|
||||
return "uint16_t";
|
||||
case proto_field::BitType::UINT32:
|
||||
return "uint32_t";
|
||||
case proto_field::BitType::UINT64:
|
||||
return "uint64_t";
|
||||
}
|
||||
}
|
||||
|
||||
switch( field.type )
|
||||
{
|
||||
case proto_field::Type::NONE:
|
||||
throw_parse_error( file, field.type_name, "invalid type" );
|
||||
|
||||
case proto_field::Type::STRING:
|
||||
return get_container_type( field.options, message.options, file.options, option_string_type,
|
||||
"char", "std::string" );
|
||||
case proto_field::Type::BYTES:
|
||||
return get_container_type( field.options, message.options, file.options, option_bytes_type,
|
||||
"std::byte", "std::vector<$>" );
|
||||
case proto_field::Type::ENUM:
|
||||
case proto_field::Type::MESSAGE:
|
||||
return replace( field.type_name, ".", "::" );
|
||||
|
||||
case proto_field::Type::FLOAT:
|
||||
return "float";
|
||||
case proto_field::Type::DOUBLE:
|
||||
return "double";
|
||||
case proto_field::Type::BOOL:
|
||||
return "bool";
|
||||
case proto_field::Type::SFIXED32:
|
||||
case proto_field::Type::INT32:
|
||||
case proto_field::Type::SINT32:
|
||||
return "int32_t";
|
||||
case proto_field::Type::FIXED32:
|
||||
case proto_field::Type::UINT32:
|
||||
return "uint32_t";
|
||||
case proto_field::Type::SFIXED64:
|
||||
case proto_field::Type::INT64:
|
||||
case proto_field::Type::SINT64:
|
||||
return "int64_t";
|
||||
case proto_field::Type::UINT64:
|
||||
case proto_field::Type::FIXED64:
|
||||
return "uint64_t";
|
||||
}
|
||||
|
||||
throw_parse_error( file, field.type_name, "invalid type" );
|
||||
}
|
||||
|
||||
void dump_field_type_and_name( std::ostream & stream, const proto_field & field,
|
||||
const proto_message & message, const proto_file & file )
|
||||
{
|
||||
const auto ctype = convert_to_ctype( file, field, message );
|
||||
|
||||
switch( field.label )
|
||||
{
|
||||
case proto_field::Label::NONE:
|
||||
stream << ctype << ' ' << field.name << get_field_bits( field );
|
||||
return;
|
||||
case proto_field::Label::OPTIONAL:
|
||||
stream << get_container_type( field.options, message.options, file.options,
|
||||
option_optional_type, ctype, "std::optional<$>" );
|
||||
break;
|
||||
case proto_field::Label::REPEATED:
|
||||
stream << get_container_type( field.options, message.options, file.options,
|
||||
option_repeated_type, ctype, "std::vector<$>" );
|
||||
break;
|
||||
case proto_field::Label::PTR:
|
||||
stream << get_container_type( field.options, message.options, file.options,
|
||||
option_pointer_type, ctype, "std::unique_ptr<$>" );
|
||||
break;
|
||||
}
|
||||
if( auto bitfield = get_field_bits( field ); !bitfield.empty( ) )
|
||||
{
|
||||
throw_parse_error( file, bitfield, "bitfield can be used only with `required` label" );
|
||||
}
|
||||
stream << ' ' << field.name;
|
||||
}
|
||||
|
||||
void dump_enum_field( std::ostream & stream, const proto_base & field )
|
||||
{
|
||||
dump_comment( stream, field.comment );
|
||||
|
||||
stream << field.name << " = " << field.number << ",\n";
|
||||
}
|
||||
|
||||
void dump_enum( std::ostream & stream, const proto_enum & my_enum, const proto_message & message,
|
||||
const proto_file & file )
|
||||
{
|
||||
dump_comment( stream, my_enum.comment );
|
||||
|
||||
stream << "enum class " << my_enum.name << " : "
|
||||
<< get_enum_type( file, my_enum.options, message.options, file.options, "int32_t" )
|
||||
<< "\n{\n";
|
||||
for( const auto & field : my_enum.fields )
|
||||
{
|
||||
dump_enum_field( stream, field );
|
||||
}
|
||||
stream << "};\n";
|
||||
}
|
||||
|
||||
void dump_message_oneof( std::ostream & stream, const proto_oneof & oneof, const proto_file & file )
|
||||
{
|
||||
dump_comment( stream, oneof.comment );
|
||||
|
||||
auto put_comma = false;
|
||||
stream << "std::variant< ";
|
||||
for( const auto & field : oneof.fields )
|
||||
{
|
||||
if( put_comma )
|
||||
{
|
||||
stream << ", ";
|
||||
}
|
||||
|
||||
stream << convert_to_ctype( file, field );
|
||||
put_comma = true;
|
||||
}
|
||||
stream << " > " << oneof.name << ";\n";
|
||||
}
|
||||
|
||||
void dump_message_map( std::ostream & stream, const proto_map & map, const proto_file & file )
|
||||
{
|
||||
dump_comment( stream, map.comment );
|
||||
|
||||
stream << "std::map< " << convert_to_ctype( file, map.key ) << ", "
|
||||
<< convert_to_ctype( file, map.value ) << " > ";
|
||||
stream << map.name << ";\n";
|
||||
}
|
||||
|
||||
void dump_default_value( std::ostream & stream, const proto_field & field )
|
||||
{
|
||||
if( auto p_index = field.options.find( "default" ); p_index != field.options.end( ) )
|
||||
{
|
||||
if( field.type == proto_field::Type::ENUM )
|
||||
{
|
||||
stream << " = " << replace( field.type_name, ".", "::" ) << "::" << p_index->second;
|
||||
}
|
||||
else if( field.type == proto_field::Type::STRING &&
|
||||
( p_index->second.size( ) < 2 || p_index->second.front( ) != '"' ||
|
||||
p_index->second.back( ) != '"' ) )
|
||||
{
|
||||
stream << " = \"" << p_index->second << "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << " = " << p_index->second << type_literal_suffix( field.type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_deprecated_attribute( std::ostream & stream, const proto_field & field )
|
||||
{
|
||||
if( auto p_index = field.options.find( "deprecated" );
|
||||
p_index != field.options.end( ) && p_index->second == "true" )
|
||||
{
|
||||
stream << "[[deprecated]] ";
|
||||
}
|
||||
}
|
||||
|
||||
void dump_message_field( std::ostream & stream, const proto_field & field,
|
||||
const proto_message & message, const proto_file & file )
|
||||
{
|
||||
dump_comment( stream, field.comment );
|
||||
dump_deprecated_attribute( stream, field );
|
||||
dump_field_type_and_name( stream, field, message, file );
|
||||
dump_default_value( stream, field );
|
||||
stream << ";\n";
|
||||
}
|
||||
|
||||
void dump_forwards( std::ostream & stream, const forwarded_declarations & forwards )
|
||||
{
|
||||
for( const auto & forward : forwards )
|
||||
{
|
||||
stream << "struct " << forward << ";\n";
|
||||
}
|
||||
if( !forwards.empty( ) )
|
||||
{
|
||||
stream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void dump_message( std::ostream & stream, const proto_message & message, const proto_file & file )
|
||||
{
|
||||
dump_comment( stream, message.comment );
|
||||
|
||||
stream << "struct " << message.name << "\n{\n";
|
||||
|
||||
dump_forwards( stream, message.forwards );
|
||||
for( const auto & sub_enum : message.enums )
|
||||
{
|
||||
dump_enum( stream, sub_enum, message, file );
|
||||
}
|
||||
|
||||
for( const auto & sub_message : message.messages )
|
||||
{
|
||||
dump_message( stream, sub_message, file );
|
||||
}
|
||||
|
||||
for( const auto & field : message.fields )
|
||||
{
|
||||
dump_message_field( stream, field, message, file );
|
||||
}
|
||||
|
||||
for( const auto & map : message.maps )
|
||||
{
|
||||
dump_message_map( stream, map, file );
|
||||
}
|
||||
|
||||
for( const auto & oneof : message.oneofs )
|
||||
{
|
||||
dump_message_oneof( stream, oneof, file );
|
||||
}
|
||||
|
||||
//- TODO: is this used in any way?
|
||||
// stream << "auto operator == ( const " << message.name << " & ) const noexcept ->
|
||||
// bool = default;\n"; stream << "auto operator != ( const " << message.name << " &
|
||||
// ) const noexcept -> bool = default;\n";
|
||||
stream << "};\n";
|
||||
}
|
||||
|
||||
void dump_messages( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
dump_forwards( stream, file.package.forwards );
|
||||
for( const auto & message : file.package.messages )
|
||||
{
|
||||
dump_message( stream, message, file );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_enums( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
for( const auto & my_enum : file.package.enums )
|
||||
{
|
||||
dump_enum( stream, my_enum, file.package, file );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_package_begin( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
dump_comment( stream, file.package.comment );
|
||||
if( !file.package.name.empty( ) )
|
||||
{
|
||||
stream << "namespace " << replace( file.package.name, ".", "::" ) << "\n{\n";
|
||||
}
|
||||
}
|
||||
|
||||
void dump_package_end( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
if( !file.package.name.empty( ) )
|
||||
{
|
||||
stream << "}// namespace " << replace( file.package.name, ".", "::" ) << "\n\n";
|
||||
}
|
||||
}
|
||||
}// namespace
|
||||
|
||||
void throw_parse_error( const proto_file & file, std::string_view at, std::string_view message )
|
||||
{
|
||||
auto stream = spb::char_stream( file.content );
|
||||
stream.skip_to( at.data( ) );
|
||||
stream.throw_parse_error( message );
|
||||
}
|
||||
|
||||
void dump_cpp_definitions( const proto_file & file, std::ostream & stream )
|
||||
{
|
||||
auto includes = cpp_includes( );
|
||||
dump_pragma( stream, file );
|
||||
get_imports( includes, file );
|
||||
get_std_includes( includes, file );
|
||||
get_user_includes( includes, file );
|
||||
dump_includes( stream, includes );
|
||||
dump_syntax( stream, file );
|
||||
dump_package_begin( stream, file );
|
||||
dump_enums( stream, file );
|
||||
dump_messages( stream, file );
|
||||
dump_package_end( stream, file );
|
||||
}
|
||||
|
||||
auto replace( std::string_view input, std::string_view what, std::string_view with ) -> std::string
|
||||
{
|
||||
auto result = std::string( input );
|
||||
auto pos = size_t{ };
|
||||
|
||||
while( ( pos = result.find( what, pos ) ) != std::string::npos )
|
||||
{
|
||||
result.replace( pos, what.size( ), with );
|
||||
pos += with.size( );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
44
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/header.h
vendored
Normal file
44
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/header.h
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
/***************************************************************************\
|
||||
* Name : C++ header dumper *
|
||||
* Description : generate C++ header with all structs and enums *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-file.h"
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* @brief dump C++ header file for parsed proto
|
||||
* dump all structs and enums
|
||||
*
|
||||
* @param file parsed proto
|
||||
* @param stream output stream
|
||||
*/
|
||||
void dump_cpp_definitions( const proto_file & file, std::ostream & stream );
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of a substring in a given string with another substring.
|
||||
*
|
||||
* @param input the original string
|
||||
* @param what the substring to be replaced
|
||||
* @param with the substring to replace 'what' with
|
||||
*
|
||||
* @return the modified string after replacements
|
||||
*/
|
||||
auto replace( std::string_view input, std::string_view what, std::string_view with ) -> std::string;
|
||||
|
||||
/**
|
||||
* @brief throw parse error 'at' offset
|
||||
*
|
||||
* @param file parsed file
|
||||
* @param at error offset
|
||||
* @param message message to the user
|
||||
*/
|
||||
[[noreturn]] void throw_parse_error( const proto_file & file, std::string_view at,
|
||||
std::string_view message );
|
||||
539
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/json/dumper.cpp
vendored
Normal file
539
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/json/dumper.cpp
vendored
Normal file
@ -0,0 +1,539 @@
|
||||
/***************************************************************************\
|
||||
* Name : json dumper *
|
||||
* Description : generate C++ src files for json de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dumper.h"
|
||||
#include "ast/ast.h"
|
||||
#include "ast/proto-field.h"
|
||||
#include "ast/proto-file.h"
|
||||
#include "io/file.h"
|
||||
#include "parser/parser.h"
|
||||
#include "template-h.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <spb/json/deserialize.hpp>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
auto replace( std::string_view input, std::string_view what, std::string_view with ) -> std::string
|
||||
{
|
||||
auto result = std::string( input );
|
||||
auto pos = size_t{ };
|
||||
|
||||
while( ( pos = result.find( what, pos ) ) != std::string::npos )
|
||||
{
|
||||
result.replace( pos, what.size( ), with );
|
||||
pos += with.size( );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, std::string_view type )
|
||||
{
|
||||
stream << replace( file_json_header_template, "$", type );
|
||||
}
|
||||
|
||||
auto json_name_from_options( const proto_options & options ) -> std::string_view
|
||||
{
|
||||
if( auto p_option = options.find( "json_name" ); p_option != options.end( ) )
|
||||
{
|
||||
return p_option->second;
|
||||
}
|
||||
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
auto convert_to_camelCase( std::string_view input ) -> std::string
|
||||
{
|
||||
auto result = std::string( input );
|
||||
if( !result.empty( ) )
|
||||
{
|
||||
result[ 0 ] = char( std::tolower( result[ 0 ] ) );
|
||||
}
|
||||
|
||||
if( input.find( '_' ) == std::string::npos )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto index = 0U;
|
||||
auto up = false;
|
||||
for( auto c : input )
|
||||
{
|
||||
if( c == '_' )
|
||||
{
|
||||
up = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[ index ] = char( ( up && index > 0 ) ? std::toupper( c ) : std::tolower( c ) );
|
||||
index += 1;
|
||||
up = false;
|
||||
}
|
||||
}
|
||||
result.resize( index );
|
||||
return result;
|
||||
}
|
||||
|
||||
auto json_field_name( const proto_base & field ) -> std::string
|
||||
{
|
||||
if( const auto result = json_name_from_options( field.options ); !result.empty( ) )
|
||||
{
|
||||
return std::string( result );
|
||||
}
|
||||
|
||||
return std::string( field.name );
|
||||
}
|
||||
|
||||
auto json_field_name_or_camelCase( const proto_base & field ) -> std::string
|
||||
{
|
||||
if( const auto result = json_name_from_options( field.options ); !result.empty( ) )
|
||||
{
|
||||
return std::string( result );
|
||||
}
|
||||
|
||||
return convert_to_camelCase( field.name );
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_message & message,
|
||||
std::string_view parent )
|
||||
{
|
||||
const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
|
||||
dump_prototypes( stream, message_with_parent );
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_enum & my_enum, std::string_view parent )
|
||||
{
|
||||
const auto enum_with_parent = std::string( parent ) + "::" + std::string( my_enum.name );
|
||||
dump_prototypes( stream, enum_with_parent );
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_enums & enums, std::string_view parent )
|
||||
{
|
||||
for( const auto & my_enum : enums )
|
||||
{
|
||||
dump_prototypes( stream, my_enum, parent );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_messages & messages,
|
||||
std::string_view parent )
|
||||
{
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
dump_prototypes( stream, message, parent );
|
||||
}
|
||||
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
if( message.messages.empty( ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
|
||||
dump_prototypes( stream, message.messages, message_with_parent );
|
||||
}
|
||||
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
if( message.enums.empty( ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
|
||||
dump_prototypes( stream, message.enums, message_with_parent );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
const auto package_name = replace( file.package.name, ".", "::" );
|
||||
dump_prototypes( stream, file.package.messages, package_name );
|
||||
dump_prototypes( stream, file.package.enums, package_name );
|
||||
}
|
||||
|
||||
void dump_cpp_includes( std::ostream & stream, std::string_view header_file_path )
|
||||
{
|
||||
stream << "#include \"" << header_file_path << "\"\n"
|
||||
<< "#include <spb/json.hpp>\n"
|
||||
"#include <system_error>\n"
|
||||
"#include <type_traits>\n\n";
|
||||
}
|
||||
|
||||
void dump_cpp_close_namespace( std::ostream & stream, std::string_view name )
|
||||
{
|
||||
stream << "} // namespace " << name << "\n";
|
||||
}
|
||||
|
||||
void dump_cpp_open_namespace( std::ostream & stream, std::string_view name )
|
||||
{
|
||||
stream << "namespace " << name << "\n{\n";
|
||||
}
|
||||
|
||||
void dump_cpp_serialize_value( std::ostream & stream, const proto_oneof & oneof )
|
||||
{
|
||||
stream << "\t{\n\t\tconst auto index = value." << oneof.name << ".index( );\n";
|
||||
stream << "\t\tswitch( index )\n\t\t{\n";
|
||||
for( size_t i = 0; i < oneof.fields.size( ); ++i )
|
||||
{
|
||||
stream << "\t\t\tcase " << i << ":\n\t\t\t\treturn stream.serialize( \""
|
||||
<< json_field_name( oneof.fields[ i ] ) << "\"sv, std::get< " << i << " >( value."
|
||||
<< oneof.name << ") );\n";
|
||||
}
|
||||
stream << "\t\t}\n\t}\n\n";
|
||||
}
|
||||
|
||||
void dump_cpp_serialize_value( std::ostream & stream, const proto_enum & my_enum,
|
||||
std::string_view full_name )
|
||||
{
|
||||
if( my_enum.fields.empty( ) )
|
||||
{
|
||||
stream << "void serialize_value( detail::ostream &, const " << full_name << " & )\n{\n";
|
||||
stream << "\treturn ;\n}\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
stream << "void serialize_value( detail::ostream & stream, const " << full_name
|
||||
<< " & value )\n{\n";
|
||||
stream << "\tswitch( value )\n\t{\n";
|
||||
|
||||
std::set< int32_t > numbers_taken;
|
||||
for( const auto & field : my_enum.fields )
|
||||
{
|
||||
if( !numbers_taken.insert( field.number ).second )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
stream << "\tcase " << full_name << "::" << field.name
|
||||
<< ":\n\t\treturn stream.serialize( \"" << field.name << "\"sv);\n";
|
||||
}
|
||||
stream << "\tdefault:\n\t\tthrow std::system_error( std::make_error_code( "
|
||||
"std::errc::invalid_argument ) );\n";
|
||||
stream << "\t}\n}\n\n";
|
||||
}
|
||||
|
||||
void dump_cpp_deserialize_value( std::ostream & stream, const proto_enum & my_enum,
|
||||
std::string_view full_name )
|
||||
{
|
||||
if( my_enum.fields.empty( ) )
|
||||
{
|
||||
stream << "void deserialize_value( detail::istream &, " << full_name << " & )\n{\n";
|
||||
stream << "\n}\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
size_t key_size_min = UINT32_MAX;
|
||||
size_t key_size_max = 0;
|
||||
|
||||
auto name_map = std::multimap< uint32_t, std::string_view >( );
|
||||
for( const auto & field : my_enum.fields )
|
||||
{
|
||||
name_map.emplace( spb::json::detail::djb2_hash( field.name ), field.name );
|
||||
key_size_min = std::min( key_size_min, field.name.size( ) );
|
||||
key_size_max = std::max( key_size_max, field.name.size( ) );
|
||||
}
|
||||
|
||||
stream << "void deserialize_value( detail::istream & stream, " << full_name
|
||||
<< " & value )\n{\n";
|
||||
stream << "\tauto enum_value = stream.deserialize_string_or_int( " << key_size_min << ", "
|
||||
<< key_size_max << " );\n";
|
||||
stream << "\tstd::visit( detail::overloaded{\n\t\t[&]( std::string_view enum_str )\n\t\t{\n";
|
||||
stream << "\t\t\tconst auto enum_hash = djb2_hash( enum_str );\n";
|
||||
stream << "\t\t\tswitch( enum_hash )\n\t\t\t{\n";
|
||||
auto last_hash = name_map.begin( )->first + 1;
|
||||
auto put_break = false;
|
||||
for( const auto & [ hash, name ] : name_map )
|
||||
{
|
||||
if( hash != last_hash )
|
||||
{
|
||||
last_hash = hash;
|
||||
if( put_break )
|
||||
{
|
||||
stream << "\t\t\t\tbreak ;\n";
|
||||
}
|
||||
put_break = true;
|
||||
stream << "\t\t\tcase detail::djb2_hash( \"" << name << "\"sv ):\n";
|
||||
}
|
||||
stream << "\t\t\t\tif( enum_str == \"" << name << "\"sv ){\n\t\t\t\t\tvalue = " << full_name
|
||||
<< "::" << name << ";\n\t\t\t\t\treturn ;\t\t\t\t}\n";
|
||||
}
|
||||
stream << "\t\t\t\tbreak ;\n";
|
||||
stream << "\t\t\t}\n\t\t\tthrow std::system_error( std::make_error_code( "
|
||||
"std::errc::invalid_argument ) );\n";
|
||||
stream << "\t\t},\n\t\t[&]( int32_t enum_int )\n\t\t{\n\t\t\tswitch( " << full_name
|
||||
<< "( enum_int ) )\n\t\t\t{\n";
|
||||
std::set< int32_t > numbers_taken;
|
||||
for( const auto & field : my_enum.fields )
|
||||
{
|
||||
if( !numbers_taken.insert( field.number ).second )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
stream << "\t\t\tcase " << full_name << "::" << field.name << ":\n";
|
||||
}
|
||||
stream << "\t\t\t\tvalue = " << full_name << "( enum_int );\n\t\t\t\treturn ;\n";
|
||||
stream << "\t\t\t}\n\t\t\tthrow std::system_error( std::make_error_code( "
|
||||
"std::errc::invalid_argument ) );\n";
|
||||
stream << "\t\t}\n\t}, enum_value );\n}\n\n";
|
||||
}
|
||||
|
||||
void dump_cpp_serialize_value( std::ostream & stream, const proto_message & message,
|
||||
std::string_view full_name )
|
||||
{
|
||||
if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
|
||||
{
|
||||
stream << "void serialize_value( detail::ostream & , const " << full_name
|
||||
<< " & )\n{\n}\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
stream << "void serialize_value( detail::ostream & stream, const " << full_name
|
||||
<< " & value )\n{\n";
|
||||
for( const auto & field : message.fields )
|
||||
{
|
||||
stream << "\tstream.serialize( \"" << json_field_name( field ) << "\"sv, value."
|
||||
<< field.name << " );\n";
|
||||
}
|
||||
for( const auto & map : message.maps )
|
||||
{
|
||||
stream << "\tstream.serialize( \"" << map.name << "\"sv, value." << map.name << " );\n";
|
||||
}
|
||||
for( const auto & oneof : message.oneofs )
|
||||
{
|
||||
dump_cpp_serialize_value( stream, oneof );
|
||||
}
|
||||
stream << "}\n";
|
||||
}
|
||||
|
||||
void dump_cpp_deserialize_value( std::ostream & stream, const proto_message & message,
|
||||
std::string_view full_name )
|
||||
{
|
||||
if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
|
||||
{
|
||||
stream << "void deserialize_value( detail::istream &, " << full_name << " & )\n{\n";
|
||||
stream << "\n}\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
//- json deserializer needs to accept both camelCase (parsed_name) and the original field name
|
||||
struct one_field
|
||||
{
|
||||
std::string parsed_name;
|
||||
std::string_view name;
|
||||
size_t oneof_index = SIZE_MAX;
|
||||
std::string_view bitfield;
|
||||
};
|
||||
|
||||
size_t key_size_min = UINT32_MAX;
|
||||
size_t key_size_max = 0;
|
||||
|
||||
auto name_map = std::multimap< uint32_t, one_field >( );
|
||||
for( const auto & field : message.fields )
|
||||
{
|
||||
key_size_min = std::min( key_size_min, field.name.size( ) );
|
||||
key_size_max = std::max( key_size_max, field.name.size( ) );
|
||||
|
||||
const auto field_name = json_field_name_or_camelCase( field );
|
||||
name_map.emplace( spb::json::detail::djb2_hash( field_name ),
|
||||
one_field{
|
||||
.parsed_name = field_name,
|
||||
.name = field.name,
|
||||
.bitfield = field.bit_field,
|
||||
} );
|
||||
if( field_name != field.name )
|
||||
{
|
||||
key_size_min = std::min( key_size_min, field_name.size( ) );
|
||||
key_size_max = std::max( key_size_max, field_name.size( ) );
|
||||
|
||||
name_map.emplace( spb::json::detail::djb2_hash( field.name ),
|
||||
one_field{
|
||||
.parsed_name = std::string( field.name ),
|
||||
.name = field.name,
|
||||
.bitfield = field.bit_field,
|
||||
} );
|
||||
}
|
||||
}
|
||||
for( const auto & field : message.maps )
|
||||
{
|
||||
key_size_min = std::min( key_size_min, field.name.size( ) );
|
||||
key_size_max = std::max( key_size_max, field.name.size( ) );
|
||||
|
||||
const auto field_name = json_field_name_or_camelCase( field );
|
||||
name_map.emplace( spb::json::detail::djb2_hash( field_name ),
|
||||
one_field{
|
||||
.parsed_name = field_name,
|
||||
.name = field.name,
|
||||
} );
|
||||
if( field_name != field.name )
|
||||
{
|
||||
key_size_min = std::min( key_size_min, field_name.size( ) );
|
||||
key_size_max = std::max( key_size_max, field_name.size( ) );
|
||||
|
||||
name_map.emplace(
|
||||
spb::json::detail::djb2_hash( field.name ),
|
||||
one_field{ .parsed_name = std::string( field.name ), .name = field.name } );
|
||||
}
|
||||
}
|
||||
for( const auto & oneof : message.oneofs )
|
||||
{
|
||||
for( size_t i = 0; i < oneof.fields.size( ); ++i )
|
||||
{
|
||||
key_size_min = std::min( key_size_min, oneof.fields[ i ].name.size( ) );
|
||||
key_size_max = std::max( key_size_max, oneof.fields[ i ].name.size( ) );
|
||||
|
||||
const auto field_name = json_field_name_or_camelCase( oneof.fields[ i ] );
|
||||
name_map.emplace( spb::json::detail::djb2_hash( field_name ),
|
||||
one_field{
|
||||
.parsed_name = field_name,
|
||||
.name = oneof.name,
|
||||
.oneof_index = i,
|
||||
} );
|
||||
if( field_name != oneof.fields[ i ].name )
|
||||
{
|
||||
key_size_min = std::min( key_size_min, field_name.size( ) );
|
||||
key_size_max = std::max( key_size_max, field_name.size( ) );
|
||||
|
||||
name_map.emplace( spb::json::detail::djb2_hash( oneof.fields[ i ].name ),
|
||||
one_field{
|
||||
.parsed_name = std::string( oneof.fields[ i ].name ),
|
||||
.name = oneof.name,
|
||||
.oneof_index = i,
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream << "void deserialize_value( detail::istream & stream, " << full_name
|
||||
<< " & value )\n{\n";
|
||||
stream << "\tauto key = stream.deserialize_key( " << key_size_min << ", " << key_size_max
|
||||
<< " );\n";
|
||||
stream << "\tswitch( djb2_hash( key ) )\n\t{\n";
|
||||
|
||||
auto last_hash = name_map.begin( )->first + 1;
|
||||
auto put_break = false;
|
||||
for( const auto & [ hash, field ] : name_map )
|
||||
{
|
||||
if( hash != last_hash )
|
||||
{
|
||||
if( put_break )
|
||||
{
|
||||
stream << "\t\t\t\tbreak;\n";
|
||||
}
|
||||
put_break = true;
|
||||
last_hash = hash;
|
||||
stream << "\t\tcase detail::djb2_hash( \"" << field.parsed_name << "\"sv ):\n";
|
||||
}
|
||||
stream << "\t\t\tif( key == \"" << field.parsed_name << "\"sv )\n\t\t\t{\n";
|
||||
if( field.oneof_index == SIZE_MAX )
|
||||
{
|
||||
if( !field.bitfield.empty( ) )
|
||||
{
|
||||
|
||||
stream << "\t\t\t\tvalue." << field.name
|
||||
<< " = stream.deserialize_bitfield< decltype( value." << field.name
|
||||
<< " ) >( " << field.bitfield << " );\n\t\t\t\treturn ;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << "\t\t\t\treturn stream.deserialize( value." << field.name << " );\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << "\t\t\t\treturn stream.deserialize_variant<" << field.oneof_index
|
||||
<< ">( value." << field.name << " );\n";
|
||||
}
|
||||
stream << "\t\t\t}\n";
|
||||
}
|
||||
stream << "\t\t\tbreak;\n\t}\n\treturn stream.skip_value( );\n}\n";
|
||||
}
|
||||
|
||||
void dump_cpp_enum( std::ostream & stream, const proto_enum & my_enum, std::string_view parent )
|
||||
{
|
||||
const auto full_name = std::string( parent ) + "::" + std::string( my_enum.name );
|
||||
dump_cpp_open_namespace( stream, "detail" );
|
||||
dump_cpp_serialize_value( stream, my_enum, full_name );
|
||||
dump_cpp_deserialize_value( stream, my_enum, full_name );
|
||||
dump_cpp_close_namespace( stream, "detail" );
|
||||
}
|
||||
|
||||
void dump_cpp_enums( std::ostream & stream, const proto_enums & enums, std::string_view parent )
|
||||
{
|
||||
for( const auto & my_enum : enums )
|
||||
{
|
||||
dump_cpp_enum( stream, my_enum, parent );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_cpp_messages( std::ostream & stream, const proto_messages & messages,
|
||||
std::string_view parent );
|
||||
|
||||
void dump_cpp_message( std::ostream & stream, const proto_message & message,
|
||||
std::string_view parent )
|
||||
{
|
||||
const auto full_name = std::string( parent ) + "::" + std::string( message.name );
|
||||
|
||||
dump_cpp_enums( stream, message.enums, full_name );
|
||||
|
||||
dump_cpp_open_namespace( stream, "detail" );
|
||||
dump_cpp_serialize_value( stream, message, full_name );
|
||||
dump_cpp_deserialize_value( stream, message, full_name );
|
||||
dump_cpp_close_namespace( stream, "detail" );
|
||||
|
||||
dump_cpp_messages( stream, message.messages, full_name );
|
||||
}
|
||||
|
||||
void dump_cpp_messages( std::ostream & stream, const proto_messages & messages,
|
||||
std::string_view parent )
|
||||
{
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
dump_cpp_message( stream, message, parent );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_cpp( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
const auto str_namespace = "::" + replace( file.package.name, ".", "::" );
|
||||
dump_cpp_enums( stream, file.package.enums, str_namespace );
|
||||
dump_cpp_messages( stream, file.package.messages, str_namespace );
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
void dump_json_header( const proto_file & file, std::ostream & stream )
|
||||
{
|
||||
dump_cpp_open_namespace( stream, "spb::json::detail" );
|
||||
stream << "struct ostream;\nstruct istream;\n";
|
||||
dump_prototypes( stream, file );
|
||||
dump_cpp_close_namespace( stream, "spb::json::detail" );
|
||||
}
|
||||
|
||||
void dump_json_cpp( const proto_file & file, const std::filesystem::path & header_file,
|
||||
std::ostream & stream )
|
||||
{
|
||||
dump_cpp_includes( stream, header_file.string( ) );
|
||||
dump_cpp_open_namespace( stream, "spb::json" );
|
||||
dump_cpp( stream, file );
|
||||
dump_cpp_close_namespace( stream, "spb::json" );
|
||||
}
|
||||
33
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/json/dumper.h
vendored
Normal file
33
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/json/dumper.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/***************************************************************************\
|
||||
* Name : json dumper *
|
||||
* Description : generate C++ src files for json de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-file.h"
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* @brief dump C++ header file for parsed proto
|
||||
*
|
||||
* @param file parsed proto
|
||||
* @param stream output stream
|
||||
* @return C++ header file for a ast
|
||||
*/
|
||||
void dump_json_header( const proto_file & file, std::ostream & stream );
|
||||
|
||||
/**
|
||||
* @brief dump C++ file for parsed proto
|
||||
*
|
||||
* @param file parsed proto
|
||||
* @param header_file generated C++ header file (ex: my.pb.h)
|
||||
* @param stream output stream
|
||||
*/
|
||||
void dump_json_cpp( const proto_file & file, const std::filesystem::path & header_file,
|
||||
std::ostream & stream );
|
||||
19
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/json/template-h.h
vendored
Normal file
19
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/json/template-h.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/***************************************************************************\
|
||||
* Name : json dumper *
|
||||
* Description : C++ template used for json de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
constexpr std::string_view file_json_header_template = R"(
|
||||
void serialize_value( ostream & stream, const $ & value );
|
||||
void deserialize_value( istream & stream, $ & value );
|
||||
|
||||
)";
|
||||
310
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/pb/dumper.cpp
vendored
Normal file
310
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/pb/dumper.cpp
vendored
Normal file
@ -0,0 +1,310 @@
|
||||
/***************************************************************************\
|
||||
* Name : pb dumper *
|
||||
* Description : generate C++ src files for pb de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dumper.h"
|
||||
#include "ast/ast-types.h"
|
||||
#include "ast/proto-field.h"
|
||||
#include "ast/proto-file.h"
|
||||
#include "template-h.h"
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
auto replace( std::string_view input, std::string_view what, std::string_view with ) -> std::string
|
||||
{
|
||||
auto result = std::string( input );
|
||||
auto pos = size_t{ };
|
||||
|
||||
while( ( pos = result.find( what, pos ) ) != std::string::npos )
|
||||
{
|
||||
result.replace( pos, what.size( ), with );
|
||||
pos += with.size( );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, std::string_view type )
|
||||
{
|
||||
stream << replace( file_pb_header_template, "$", type );
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_message & message,
|
||||
std::string_view parent )
|
||||
{
|
||||
const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
|
||||
dump_prototypes( stream, message_with_parent );
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_messages & messages,
|
||||
std::string_view parent )
|
||||
{
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
dump_prototypes( stream, message, parent );
|
||||
}
|
||||
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
if( message.messages.empty( ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto message_with_parent = std::string( parent ) + "::" + std::string( message.name );
|
||||
dump_prototypes( stream, message.messages, message_with_parent );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_prototypes( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
const auto package_name = replace( file.package.name, ".", "::" );
|
||||
dump_prototypes( stream, file.package.messages, package_name );
|
||||
}
|
||||
|
||||
void dump_cpp_includes( std::ostream & stream, std::string_view header_file_path )
|
||||
{
|
||||
stream << "#include \"" << header_file_path << "\"\n"
|
||||
<< "#include <spb/pb.hpp>\n"
|
||||
"#include <type_traits>\n\n";
|
||||
}
|
||||
|
||||
void dump_cpp_close_namespace( std::ostream & stream, std::string_view name )
|
||||
{
|
||||
stream << "} // namespace " << name << "\n";
|
||||
}
|
||||
|
||||
void dump_cpp_open_namespace( std::ostream & stream, std::string_view name )
|
||||
{
|
||||
stream << "namespace " << name << "\n{\n";
|
||||
}
|
||||
|
||||
auto encoder_type_str( const proto_file & file, const proto_field & field ) -> std::string
|
||||
{
|
||||
switch( field.type )
|
||||
{
|
||||
case proto_field::Type::NONE:
|
||||
case proto_field::Type::BYTES:
|
||||
case proto_field::Type::MESSAGE:
|
||||
case proto_field::Type::STRING:
|
||||
return { };
|
||||
|
||||
case proto_field::Type::BOOL:
|
||||
case proto_field::Type::ENUM:
|
||||
case proto_field::Type::INT32:
|
||||
case proto_field::Type::UINT32:
|
||||
case proto_field::Type::INT64:
|
||||
case proto_field::Type::UINT64:
|
||||
return is_packed_array( file, field ) ? "scalar_encoder::varint | scalar_encoder::packed"
|
||||
: "scalar_encoder::varint";
|
||||
|
||||
case proto_field::Type::SINT32:
|
||||
case proto_field::Type::SINT64:
|
||||
return is_packed_array( file, field ) ? "scalar_encoder::svarint | scalar_encoder::packed"
|
||||
: "scalar_encoder::svarint";
|
||||
|
||||
case proto_field::Type::FLOAT:
|
||||
case proto_field::Type::FIXED32:
|
||||
case proto_field::Type::SFIXED32:
|
||||
return is_packed_array( file, field ) ? "scalar_encoder::i32 | scalar_encoder::packed"
|
||||
: "scalar_encoder::i32";
|
||||
|
||||
case proto_field::Type::DOUBLE:
|
||||
case proto_field::Type::FIXED64:
|
||||
case proto_field::Type::SFIXED64:
|
||||
return is_packed_array( file, field ) ? "scalar_encoder::i64 | scalar_encoder::packed"
|
||||
: "scalar_encoder::i64";
|
||||
}
|
||||
return { };
|
||||
}
|
||||
|
||||
auto encoder_type( const proto_file & file, const proto_field & field ) -> std::string
|
||||
{
|
||||
const auto encoder = encoder_type_str( file, field );
|
||||
if( encoder.empty( ) )
|
||||
return { };
|
||||
|
||||
return "_as<" + encoder + ">";
|
||||
}
|
||||
|
||||
auto map_encoder_type( const proto_file & file, const proto_field & key, const proto_field & value )
|
||||
-> std::string
|
||||
{
|
||||
const auto key_encoder = encoder_type_str( file, key );
|
||||
const auto value_encoder = encoder_type_str( file, value );
|
||||
|
||||
return "_as< scalar_encoder_combine( " +
|
||||
( key_encoder.empty( ) ? std::string( "{}" ) : key_encoder ) + ", " +
|
||||
( value_encoder.empty( ) ? std::string( "{}" ) : value_encoder ) + " ) >";
|
||||
}
|
||||
|
||||
auto bitfield_encoder_type( const proto_file & file, const proto_field & field ) -> std::string
|
||||
{
|
||||
const auto encoder = encoder_type_str( file, field );
|
||||
return "_as< " + encoder + ", decltype( value." + std::string( field.name ) + ") >";
|
||||
}
|
||||
|
||||
void dump_cpp_serialize_value( std::ostream & stream, const proto_file & file,
|
||||
const proto_oneof & oneof )
|
||||
{
|
||||
stream << "\t{\n\t\tconst auto index = value." << oneof.name << ".index( );\n";
|
||||
stream << "\t\tswitch( index )\n\t\t{\n";
|
||||
for( size_t i = 0; i < oneof.fields.size( ); ++i )
|
||||
{
|
||||
stream << "\t\t\tcase " << i << ":\n\t\t\t\treturn stream.serialize"
|
||||
<< encoder_type( file, oneof.fields[ i ] ) << "( " << oneof.fields[ i ].number
|
||||
<< ", std::get< " << i << " >( value." << oneof.name << ") );\n";
|
||||
}
|
||||
stream << "\t\t}\n\t}\n\n";
|
||||
}
|
||||
|
||||
void dump_cpp_serialize_value( std::ostream & stream, const proto_file & file,
|
||||
const proto_message & message, std::string_view full_name )
|
||||
{
|
||||
if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
|
||||
{
|
||||
stream << "void serialize( detail::ostream & , const " << full_name << " & )\n{\n}\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
stream << "void serialize( detail::ostream & stream, const " << full_name << " & value )\n{\n";
|
||||
for( const auto & field : message.fields )
|
||||
{
|
||||
stream << "\tstream.serialize" << encoder_type( file, field ) << "( " << field.number
|
||||
<< ", value." << field.name << " );\n";
|
||||
}
|
||||
for( const auto & map : message.maps )
|
||||
{
|
||||
stream << "\tstream.serialize" << map_encoder_type( file, map.key, map.value ) << "( "
|
||||
<< map.number << ", value." << map.name << " );\n";
|
||||
}
|
||||
for( const auto & oneof : message.oneofs )
|
||||
{
|
||||
dump_cpp_serialize_value( stream, file, oneof );
|
||||
}
|
||||
stream << "}\n";
|
||||
}
|
||||
|
||||
void dump_cpp_deserialize_value( std::ostream & stream, const proto_file & file,
|
||||
const proto_message & message, std::string_view full_name )
|
||||
{
|
||||
if( message.fields.empty( ) && message.maps.empty( ) && message.oneofs.empty( ) )
|
||||
{
|
||||
stream << "void deserialize_value( detail::istream & stream, " << full_name
|
||||
<< " &, uint32_t tag )\n{\n";
|
||||
stream << "\tstream.skip( tag );\n}\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
stream << "void deserialize_value( detail::istream & stream, " << full_name
|
||||
<< " & value, uint32_t tag )\n{\n";
|
||||
stream << "\tswitch( field_from_tag( tag ) )\n\t{\n";
|
||||
|
||||
for( const auto & field : message.fields )
|
||||
{
|
||||
|
||||
stream << "\t\tcase " << field.number << ":\n\t\t\t";
|
||||
if( !field.bit_field.empty( ) )
|
||||
{
|
||||
stream << "value." << field.name << " = stream.deserialize_bitfield"
|
||||
<< bitfield_encoder_type( file, field ) << "( " << field.bit_field
|
||||
<< ", tag );\n";
|
||||
stream << "\t\t\treturn;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << "return stream.deserialize" << encoder_type( file, field ) << "( value."
|
||||
<< field.name << ", tag );\n";
|
||||
}
|
||||
}
|
||||
for( const auto & map : message.maps )
|
||||
{
|
||||
stream << "\t\tcase " << map.number << ":\n\t\t\treturn ";
|
||||
stream << "\tstream.deserialize" << map_encoder_type( file, map.key, map.value )
|
||||
<< "( value." << map.name << ", tag );\n";
|
||||
}
|
||||
for( const auto & oneof : message.oneofs )
|
||||
{
|
||||
for( size_t i = 0; i < oneof.fields.size( ); ++i )
|
||||
{
|
||||
stream << "\t\tcase " << oneof.fields[ i ].number << ":\n\t\t\treturn ";
|
||||
auto type = encoder_type( file, oneof.fields[ i ] );
|
||||
if( type.empty( ) )
|
||||
{
|
||||
stream << "\tstream.deserialize_variant< " << i << ">( value." << oneof.name
|
||||
<< ", tag );\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
type.erase( 0, 4 );
|
||||
stream << "\tstream.deserialize_variant_as< " << i << ", " << type << "( value."
|
||||
<< oneof.name << ", tag );\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream << "\t\tdefault:\n\t\t\treturn stream.skip( tag );\t\n}\n}\n\n";
|
||||
}
|
||||
|
||||
void dump_cpp_messages( std::ostream & stream, const proto_file & file,
|
||||
const proto_messages & messages, std::string_view parent );
|
||||
|
||||
void dump_cpp_message( std::ostream & stream, const proto_file & file,
|
||||
const proto_message & message, std::string_view parent )
|
||||
{
|
||||
const auto full_name = std::string( parent ) + "::" + std::string( message.name );
|
||||
|
||||
dump_cpp_open_namespace( stream, "detail" );
|
||||
dump_cpp_serialize_value( stream, file, message, full_name );
|
||||
dump_cpp_deserialize_value( stream, file, message, full_name );
|
||||
dump_cpp_close_namespace( stream, "detail" );
|
||||
|
||||
dump_cpp_messages( stream, file, message.messages, full_name );
|
||||
}
|
||||
|
||||
void dump_cpp_messages( std::ostream & stream, const proto_file & file,
|
||||
const proto_messages & messages, std::string_view parent )
|
||||
{
|
||||
for( const auto & message : messages )
|
||||
{
|
||||
dump_cpp_message( stream, file, message, parent );
|
||||
}
|
||||
}
|
||||
|
||||
void dump_cpp( std::ostream & stream, const proto_file & file )
|
||||
{
|
||||
const auto str_namespace = "::" + replace( file.package.name, ".", "::" );
|
||||
dump_cpp_messages( stream, file, file.package.messages, str_namespace );
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
void dump_pb_header( const proto_file & file, std::ostream & stream )
|
||||
{
|
||||
dump_cpp_open_namespace( stream, "spb::pb::detail" );
|
||||
stream << "struct ostream;\nstruct istream;\n";
|
||||
dump_prototypes( stream, file );
|
||||
dump_cpp_close_namespace( stream, "spb::pb::detail" );
|
||||
}
|
||||
|
||||
void dump_pb_cpp( const proto_file & file, const std::filesystem::path & header_file,
|
||||
std::ostream & stream )
|
||||
{
|
||||
dump_cpp_includes( stream, header_file.string( ) );
|
||||
dump_cpp_open_namespace( stream, "spb::pb" );
|
||||
dump_cpp( stream, file );
|
||||
dump_cpp_close_namespace( stream, "spb::pb" );
|
||||
}
|
||||
33
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/pb/dumper.h
vendored
Normal file
33
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/pb/dumper.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/***************************************************************************\
|
||||
* Name : protobuf dumper *
|
||||
* Description : generate C++ src files for protobuf de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-file.h"
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* @brief dump C++ header file for parsed proto
|
||||
*
|
||||
* @param file parsed proto
|
||||
* @param stream output stream
|
||||
* @return C++ header file for a ast
|
||||
*/
|
||||
void dump_pb_header( const proto_file & file, std::ostream & stream );
|
||||
|
||||
/**
|
||||
* @brief dump C++ file for parsed proto
|
||||
*
|
||||
* @param file parsed proto
|
||||
* @param header_file generated C++ header file (ex: my.pb.h)
|
||||
* @param stream output stream
|
||||
*/
|
||||
void dump_pb_cpp( const proto_file & file, const std::filesystem::path & header_file,
|
||||
std::ostream & stream );
|
||||
19
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/pb/template-h.h
vendored
Normal file
19
3rdparty/simple-protobuf/src/spb-proto-compiler/dumper/pb/template-h.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/***************************************************************************\
|
||||
* Name : pb dumper *
|
||||
* Description : C++ template used for pb de/serialization *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
constexpr std::string_view file_pb_header_template = R"(
|
||||
void serialize( ostream & stream, const $ & value );
|
||||
void deserialize_value( istream & stream, $ & value, uint32_t tag );
|
||||
|
||||
)";
|
||||
43
3rdparty/simple-protobuf/src/spb-proto-compiler/io/file.cpp
vendored
Normal file
43
3rdparty/simple-protobuf/src/spb-proto-compiler/io/file.cpp
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/***************************************************************************\
|
||||
* Name : file io *
|
||||
* Description : basic file io *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "file.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
auto load_file( const std::filesystem::path & file_path ) -> std::string
|
||||
{
|
||||
const auto file_size = std::filesystem::file_size( file_path );
|
||||
auto file_content = std::string( file_size, '\0' );
|
||||
|
||||
if( auto * p_file = fopen( file_path.string( ).c_str( ), "rb" ); p_file )
|
||||
{
|
||||
const auto read = fread( file_content.data( ), 1, file_content.size( ), p_file );
|
||||
fclose( p_file );
|
||||
file_content.resize( read );
|
||||
return file_content;
|
||||
}
|
||||
throw std::runtime_error( std::string( " " ) + strerror( errno ) );
|
||||
}
|
||||
|
||||
void save_file( const std::filesystem::path & file_path, std::string_view file_content )
|
||||
{
|
||||
if( auto * p_file = fopen( file_path.string( ).c_str( ), "wb" ); p_file )
|
||||
{
|
||||
const auto written = fwrite( file_content.data( ), 1, file_content.size( ), p_file );
|
||||
fclose( p_file );
|
||||
if( written == file_content.size( ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error( std::string( " " ) + strerror( errno ) );
|
||||
}
|
||||
31
3rdparty/simple-protobuf/src/spb-proto-compiler/io/file.h
vendored
Normal file
31
3rdparty/simple-protobuf/src/spb-proto-compiler/io/file.h
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/***************************************************************************\
|
||||
* Name : file io *
|
||||
* Description : basic file io *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* @brief loads file from file_path
|
||||
*
|
||||
* @param file_path files path
|
||||
* @return file's content
|
||||
*/
|
||||
auto load_file( const std::filesystem::path & file_path ) -> std::string;
|
||||
|
||||
/**
|
||||
* @brief save file_content to a file_path
|
||||
*
|
||||
* @param file_path files path
|
||||
* @param file_content files content
|
||||
*/
|
||||
void save_file( const std::filesystem::path & file_path, std::string_view file_content );
|
||||
142
3rdparty/simple-protobuf/src/spb-proto-compiler/main.cpp
vendored
Normal file
142
3rdparty/simple-protobuf/src/spb-proto-compiler/main.cpp
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
/***************************************************************************\
|
||||
* Name : spb-protoc *
|
||||
* Description : proto file compiler *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "dumper/dumper.h"
|
||||
#include "parser/parser.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace std::literals;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
constexpr auto opt_version = "--version"sv;
|
||||
constexpr auto opt_v = "-v"sv;
|
||||
|
||||
constexpr auto opt_help = "--help"sv;
|
||||
constexpr auto opt_h = "-h"sv;
|
||||
|
||||
constexpr auto opt_cpp_out = "--cpp_out="sv;
|
||||
|
||||
constexpr auto opt_proto_path = "--proto_path="sv;
|
||||
constexpr auto opt_ipath = "-IPATH="sv;
|
||||
|
||||
void print_usage( )
|
||||
{
|
||||
std::cout
|
||||
<< "Usage: spb-protoc [OPTION] PROTO_FILES\n"
|
||||
<< "Parse PROTO_FILES and generate C++ source files.\n"
|
||||
<< " -IPATH=, --proto_path=PATH Specify the directory in which to search for imports.\n"
|
||||
<< "May be specified multiple times. directories will be searched in order.\n"
|
||||
<< " -v, --version Show version info and exit.\n"
|
||||
<< " -h, --help Show this text and exit.\n"
|
||||
<< " --cpp_out=OUT_DIR Generate C++ header and source.\n\n";
|
||||
}
|
||||
|
||||
void process_file( const fs::path & input_file, std::span< const fs::path > import_paths,
|
||||
const fs::path & output_dir )
|
||||
{
|
||||
const auto parsed_file = parse_proto_file( input_file, import_paths );
|
||||
const auto output_cpp_header = cpp_file_name_from_proto( input_file, ".pb.h" );
|
||||
const auto output_cpp = cpp_file_name_from_proto( input_file, ".pb.cc" );
|
||||
|
||||
auto cpp_header_stream = std::ofstream( output_dir / output_cpp_header );
|
||||
dump_cpp_header( parsed_file, cpp_header_stream );
|
||||
|
||||
auto cpp_stream = std::ofstream( output_dir / output_cpp );
|
||||
dump_cpp( parsed_file, output_cpp_header, cpp_stream );
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
auto main( int argc, char * argv[] ) -> int
|
||||
{
|
||||
if( argc < 2 )
|
||||
{
|
||||
print_usage( );
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto output_dir = fs::path( );
|
||||
auto import_paths = std::vector< fs::path >{ fs::current_path( ) };
|
||||
|
||||
for( ; argc > 1 && argv[ 1 ][ 0 ] == '-'; argc--, argv++ )
|
||||
{
|
||||
if( opt_help == argv[ 1 ] || opt_h == argv[ 1 ] )
|
||||
{
|
||||
print_usage( );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( opt_version == argv[ 1 ] || opt_v == argv[ 1 ] )
|
||||
{
|
||||
std::cout << "spb-protoc version 0.1.0\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( std::string_view( argv[ 1 ] ).starts_with( opt_proto_path ) )
|
||||
{
|
||||
import_paths.emplace_back( argv[ 1 ] + opt_proto_path.size( ) );
|
||||
}
|
||||
else if( std::string_view( argv[ 1 ] ).starts_with( opt_ipath ) )
|
||||
{
|
||||
import_paths.emplace_back( argv[ 1 ] + opt_ipath.size( ) );
|
||||
}
|
||||
else if( std::string_view( argv[ 1 ] ).starts_with( opt_cpp_out ) )
|
||||
{
|
||||
output_dir = argv[ 1 ] + opt_cpp_out.size( );
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Unknown option: " << argv[ 1 ] << ", use -h or --help\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto input_files = std::vector< fs::path >( );
|
||||
for( auto i = 1; i < argc; i++ )
|
||||
{
|
||||
input_files.emplace_back( argv[ i ] );
|
||||
import_paths.emplace_back( input_files.back( ).parent_path( ) );
|
||||
}
|
||||
|
||||
if( output_dir.empty( ) )
|
||||
{
|
||||
std::cerr << "Missing output directory, use --cpp_out=OUT_DIR:\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( input_files.empty( ) )
|
||||
{
|
||||
std::cerr << "Missing input files, use PROTO_FILES:\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
for( const auto & input_file : input_files )
|
||||
{
|
||||
process_file( input_file, import_paths, output_dir );
|
||||
}
|
||||
}
|
||||
catch( const std::exception & e )
|
||||
{
|
||||
std::cerr << e.what( ) << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
174
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/char_stream.h
vendored
Normal file
174
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/char_stream.h
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
/***************************************************************************\
|
||||
* Name : input stream *
|
||||
* Description : char stream used for parsing proto files *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace spb
|
||||
{
|
||||
|
||||
struct char_stream
|
||||
{
|
||||
private:
|
||||
//- start of the stream
|
||||
const char * p_start = nullptr;
|
||||
//- current position in the stream
|
||||
const char * p_begin = nullptr;
|
||||
//- end of the stream
|
||||
const char * p_end = nullptr;
|
||||
//- current char
|
||||
char m_current = { };
|
||||
|
||||
/**
|
||||
* @brief gets the next char from the stream
|
||||
*
|
||||
* @param skip_white_space if true, skip white spaces
|
||||
*/
|
||||
void update_current( bool skip_white_space ) noexcept
|
||||
{
|
||||
while( p_begin < p_end )
|
||||
{
|
||||
m_current = *p_begin;
|
||||
if( !skip_white_space || isspace( m_current ) == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
p_begin += 1;
|
||||
}
|
||||
m_current = { };
|
||||
}
|
||||
|
||||
public:
|
||||
explicit char_stream( std::string_view content ) noexcept
|
||||
: p_start( content.data( ) )
|
||||
, p_begin( p_start )
|
||||
, p_end( p_begin + content.size( ) )
|
||||
{
|
||||
update_current( true );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto begin( ) const noexcept -> const char *
|
||||
{
|
||||
return p_begin;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto end( ) const noexcept -> const char *
|
||||
{
|
||||
return p_end;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto empty( ) const noexcept -> bool
|
||||
{
|
||||
return p_end <= p_begin;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto current_char( ) const noexcept -> char
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief consumes `current char` if its equal to c
|
||||
*
|
||||
* @param c consumed char
|
||||
* @return true if char was consumed
|
||||
*/
|
||||
[[nodiscard]] auto consume( char c ) noexcept -> bool
|
||||
{
|
||||
if( auto current = current_char( ); current == c )
|
||||
{
|
||||
consume_current_char( true );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief consumes an `token`
|
||||
*
|
||||
* @param token consumed `token` (whole word)
|
||||
* @return true if `token` was consumed
|
||||
*/
|
||||
auto consume( std::string_view token ) noexcept -> bool
|
||||
{
|
||||
const auto state = *this;
|
||||
|
||||
if( content( ).starts_with( token ) )
|
||||
{
|
||||
p_begin += token.size( );
|
||||
update_current( false );
|
||||
auto next = current_char( );
|
||||
if( isspace( next ) || !isalnum( next ) )
|
||||
{
|
||||
update_current( true );
|
||||
return true;
|
||||
}
|
||||
*this = state;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void consume_current_char( bool skip_white_space ) noexcept
|
||||
{
|
||||
if( begin( ) < end( ) )
|
||||
{
|
||||
p_begin += 1;
|
||||
update_current( skip_white_space );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief trim current spaces
|
||||
*
|
||||
*/
|
||||
void consume_space( ) noexcept
|
||||
{
|
||||
update_current( true );
|
||||
}
|
||||
|
||||
void skip_to( const char * ptr ) noexcept
|
||||
{
|
||||
assert( ptr >= p_start && ptr <= end( ) );
|
||||
p_begin = ptr;
|
||||
update_current( true );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto content( ) const noexcept -> std::string_view
|
||||
{
|
||||
return { begin( ), size_t( end( ) - begin( ) ) };
|
||||
}
|
||||
|
||||
[[nodiscard]] auto current_line( ) const noexcept -> size_t
|
||||
{
|
||||
return std::count( p_start, p_begin, '\n' ) + 1;
|
||||
}
|
||||
[[nodiscard]] auto current_column( ) const noexcept -> size_t
|
||||
{
|
||||
auto parsed = std::string_view( p_start, p_begin - p_start );
|
||||
|
||||
if( auto p = parsed.rfind( '\n' ); p != std::string_view::npos )
|
||||
{
|
||||
parsed.remove_prefix( p );
|
||||
}
|
||||
return std::max< size_t >( parsed.size( ), 1 );
|
||||
}
|
||||
[[noreturn]] void throw_parse_error( std::string_view message )
|
||||
{
|
||||
throw std::runtime_error( std::to_string( current_line( ) ) + ":" +
|
||||
std::to_string( current_column( ) ) + ": " +
|
||||
std::string( message ) );
|
||||
}
|
||||
};
|
||||
}// namespace spb
|
||||
33
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/options.h
vendored
Normal file
33
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/options.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/***************************************************************************\
|
||||
* Name : options *
|
||||
* Description : option names used by parser/dumper *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
const auto option_optional_type = "optional.type"sv;
|
||||
const auto option_optional_include = "optional.include"sv;
|
||||
|
||||
const auto option_repeated_type = "repeated.type"sv;
|
||||
const auto option_repeated_include = "repeated.include"sv;
|
||||
|
||||
const auto option_pointer_type = "pointer.type"sv;
|
||||
const auto option_pointer_include = "pointer.include"sv;
|
||||
|
||||
const auto option_bytes_type = "bytes.type"sv;
|
||||
const auto option_bytes_include = "bytes.include"sv;
|
||||
|
||||
const auto option_string_type = "string.type"sv;
|
||||
const auto option_string_include = "string.include"sv;
|
||||
|
||||
const auto option_field_type = "field.type"sv;
|
||||
const auto option_enum_type = "enum.type"sv;
|
||||
998
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/parser.cpp
vendored
Normal file
998
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/parser.cpp
vendored
Normal file
@ -0,0 +1,998 @@
|
||||
/***************************************************************************\
|
||||
* Name : .proto parser *
|
||||
* Description : parse proto file and constructs an ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#include "parser.h"
|
||||
#include "ast/proto-field.h"
|
||||
#include "ast/proto-file.h"
|
||||
#include "dumper/header.h"
|
||||
#include "options.h"
|
||||
#include <array>
|
||||
#include <ast/ast.h>
|
||||
#include <ast/ast-types.h>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <concepts>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <io/file.h>
|
||||
#include <parser/char_stream.h>
|
||||
#include <spb/to_from_chars.h>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
using parsed_files = std::set< std::string >;
|
||||
|
||||
[[nodiscard]] auto parse_proto_file( fs::path file, parsed_files &,
|
||||
std::span< const fs::path > import_paths,
|
||||
const fs::path & base_dir ) -> proto_file;
|
||||
|
||||
auto find_file_in_paths( const fs::path & file_name, std::span< const fs::path > import_paths,
|
||||
const fs::path & base_dir ) -> fs::path
|
||||
{
|
||||
if( file_name.has_root_path( ) )
|
||||
{
|
||||
if( fs::exists( file_name ) )
|
||||
{
|
||||
return file_name;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( fs::exists( base_dir / file_name ) )
|
||||
{
|
||||
return base_dir / file_name;
|
||||
}
|
||||
|
||||
for( const auto & import_path : import_paths )
|
||||
{
|
||||
auto file_path = import_path.has_root_path( ) ? import_path / file_name
|
||||
: base_dir / import_path / file_name;
|
||||
if( fs::exists( file_path ) )
|
||||
{
|
||||
return file_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error( strerror( ENOENT ) );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_all_imports( const proto_file & file, parsed_files & already_parsed,
|
||||
std::span< const fs::path > import_paths,
|
||||
const fs::path & base_dir ) -> proto_files
|
||||
{
|
||||
proto_files result;
|
||||
result.reserve( file.imports.size( ) );
|
||||
for( const auto & import : file.imports )
|
||||
{
|
||||
if( !already_parsed.contains( std::string( import.file_name ) ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
result.emplace_back(
|
||||
parse_proto_file( import.file_name, already_parsed, import_paths, base_dir ) );
|
||||
}
|
||||
catch( const std::runtime_error & error )
|
||||
{
|
||||
throw_parse_error( file, import.file_name, error.what( ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void parse_or_throw( bool parsed, spb::char_stream & stream, std::string_view message )
|
||||
{
|
||||
if( !parsed )
|
||||
{
|
||||
stream.throw_parse_error( message );
|
||||
}
|
||||
}
|
||||
|
||||
void consume_or_fail( spb::char_stream & stream, char c )
|
||||
{
|
||||
if( !stream.consume( c ) )
|
||||
{
|
||||
return stream.throw_parse_error( "(expecting '" + std::string( 1, c ) + "')" );
|
||||
}
|
||||
}
|
||||
|
||||
void consume_or_fail( spb::char_stream & stream, std::string_view token )
|
||||
{
|
||||
if( !stream.consume( token ) )
|
||||
{
|
||||
return stream.throw_parse_error( "(expecting '" + std::string( token ) + "')" );
|
||||
}
|
||||
}
|
||||
|
||||
void skip_white_space_until_new_line( spb::char_stream & stream )
|
||||
{
|
||||
while( ( isspace( stream.current_char( ) ) != 0 ) && stream.current_char( ) != '\n' )
|
||||
{
|
||||
stream.consume_current_char( false );
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
concept int_or_float = std::integral< T > || std::floating_point< T >;
|
||||
|
||||
auto consume_number( spb::char_stream & stream, std::integral auto & number ) -> bool
|
||||
{
|
||||
number = { };
|
||||
auto base = 10;
|
||||
if( stream.consume( '0' ) )
|
||||
{
|
||||
base = 8;
|
||||
if( stream.consume( 'x' ) || stream.consume( 'X' ) )
|
||||
{
|
||||
base = 16;
|
||||
}
|
||||
if( !::isdigit( stream.current_char( ) ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
auto result = spb_std_emu::from_chars( stream.begin( ), stream.end( ), number, base );
|
||||
if( result.ec == std::errc{ } ) [[likely]]
|
||||
{
|
||||
stream.skip_to( result.ptr );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto consume_number( spb::char_stream & stream, std::floating_point auto & number ) -> bool
|
||||
{
|
||||
auto result = spb_std_emu::from_chars( stream.begin( ), stream.end( ), number );
|
||||
if( result.ec == std::errc{ } ) [[likely]]
|
||||
{
|
||||
stream.skip_to( result.ptr );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto consume_int( spb::char_stream & stream, std::integral auto & number ) -> bool
|
||||
{
|
||||
return consume_number( stream, number );
|
||||
}
|
||||
|
||||
auto consume_float( spb::char_stream & stream, std::floating_point auto & number ) -> bool
|
||||
{
|
||||
return consume_number( stream, number );
|
||||
}
|
||||
|
||||
template < int_or_float T >
|
||||
auto parse_number( spb::char_stream & stream ) -> T
|
||||
{
|
||||
auto result = T{ };
|
||||
if( consume_number( stream, result ) )
|
||||
{
|
||||
return result;
|
||||
}
|
||||
stream.throw_parse_error( "expecting number" );
|
||||
}
|
||||
|
||||
auto parse_int_or_float( spb::char_stream & stream ) -> std::string_view
|
||||
{
|
||||
const auto * start = stream.begin( );
|
||||
auto number = double( );
|
||||
auto result = spb_std_emu::from_chars( stream.begin( ), stream.end( ), number );
|
||||
if( result.ec == std::errc{ } ) [[likely]]
|
||||
{
|
||||
stream.skip_to( result.ptr );
|
||||
return { start, static_cast< size_t >( result.ptr - start ) };
|
||||
}
|
||||
stream.throw_parse_error( "expecting number" );
|
||||
}
|
||||
|
||||
//- parse single line comment // \n
|
||||
void parse_comment_line( spb::char_stream & stream, proto_comment & comment )
|
||||
{
|
||||
const auto * start = stream.begin( );
|
||||
const auto end = stream.content( ).find( '\n' );
|
||||
if( end == std::string_view::npos )
|
||||
{
|
||||
stream.throw_parse_error( "expecting \\n" );
|
||||
}
|
||||
|
||||
comment.comments.emplace_back( start - 2, end + 2 );
|
||||
|
||||
stream.skip_to( start + end + 1 );
|
||||
}
|
||||
|
||||
//- parse multiline comment /* */
|
||||
void parse_comment_multiline( spb::char_stream & stream, proto_comment & comment )
|
||||
{
|
||||
const auto * start = stream.begin( );
|
||||
const auto end = stream.content( ).find( "*/" );
|
||||
if( end == std::string_view::npos )
|
||||
{
|
||||
stream.throw_parse_error( "expecting */" );
|
||||
}
|
||||
|
||||
comment.comments.emplace_back( start - 2, end + 4 );
|
||||
stream.skip_to( start + end + 2 );
|
||||
}
|
||||
|
||||
//- parse // \n or /**/
|
||||
auto parse_comment( spb::char_stream & stream ) -> proto_comment
|
||||
{
|
||||
auto result = proto_comment{ };
|
||||
|
||||
while( stream.current_char( ) == '/' )
|
||||
{
|
||||
stream.consume_current_char( false );
|
||||
if( stream.current_char( ) == '/' )
|
||||
{
|
||||
stream.consume_current_char( false );
|
||||
parse_comment_line( stream, result );
|
||||
}
|
||||
else if( stream.current_char( ) == '*' )
|
||||
{
|
||||
stream.consume_current_char( false );
|
||||
parse_comment_multiline( stream, result );
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.throw_parse_error( "expecting // or /*" );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//- parse ;
|
||||
[[nodiscard]] auto parse_empty_statement( spb::char_stream & stream ) -> bool
|
||||
{
|
||||
return stream.consume( ';' );
|
||||
}
|
||||
|
||||
//- parse "string" | 'string'
|
||||
[[nodiscard]] auto parse_string_literal( spb::char_stream & stream ) -> std::string_view
|
||||
{
|
||||
const auto c = stream.current_char( );
|
||||
if( c != '"' && c != '\'' )
|
||||
{
|
||||
stream.throw_parse_error( "expecting \" or '" );
|
||||
return { };
|
||||
}
|
||||
|
||||
stream.consume_current_char( false );
|
||||
const auto * start = stream.begin( );
|
||||
auto current = stream.current_char( );
|
||||
while( ( current != 0 ) && current != c )
|
||||
{
|
||||
stream.consume_current_char( false );
|
||||
current = stream.current_char( );
|
||||
}
|
||||
|
||||
if( current != c )
|
||||
{
|
||||
stream.throw_parse_error( "missing string end" );
|
||||
}
|
||||
|
||||
auto result = std::string_view( start, static_cast< size_t >( stream.begin( ) - start ) );
|
||||
stream.consume_current_char( true );
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_ident( spb::char_stream & stream, bool skip_last_white_space = true )
|
||||
-> std::string_view
|
||||
{
|
||||
const auto * start = stream.begin( );
|
||||
|
||||
if( isalpha( stream.current_char( ) ) == 0 )
|
||||
{
|
||||
stream.throw_parse_error( "expecting identifier(a-zA-Z)" );
|
||||
return { };
|
||||
}
|
||||
|
||||
stream.consume_current_char( false );
|
||||
auto current = stream.current_char( );
|
||||
while( ( current != 0 ) && ( isalnum( current ) != 0 || current == '_' ) )
|
||||
{
|
||||
stream.consume_current_char( false );
|
||||
current = stream.current_char( );
|
||||
}
|
||||
|
||||
auto result = std::string_view( start, static_cast< size_t >( stream.begin( ) - start ) );
|
||||
if( skip_last_white_space )
|
||||
{
|
||||
stream.consume_space( );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_full_ident( spb::char_stream & stream ) -> std::string_view
|
||||
{
|
||||
const auto * start = stream.begin( );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
( void ) parse_ident( stream, false );
|
||||
if( stream.current_char( ) != '.' )
|
||||
{
|
||||
break;
|
||||
}
|
||||
stream.consume_current_char( false );
|
||||
}
|
||||
auto result = std::string_view{ start, static_cast< size_t >( stream.begin( ) - start ) };
|
||||
stream.consume_space( );
|
||||
return result;
|
||||
}
|
||||
|
||||
void parse_top_level_service_body( spb::char_stream & stream, proto_file &, proto_comment && )
|
||||
{
|
||||
while( !stream.consume( '}' ) )
|
||||
stream.consume_current_char( true );
|
||||
}
|
||||
|
||||
void consume_statement_end( spb::char_stream & stream, proto_comment & comment )
|
||||
{
|
||||
if( stream.current_char( ) != ';' )
|
||||
{
|
||||
return stream.throw_parse_error( R"(expecting ";")" );
|
||||
}
|
||||
stream.consume_current_char( false );
|
||||
skip_white_space_until_new_line( stream );
|
||||
if( stream.current_char( ) == '/' )
|
||||
{
|
||||
stream.consume_current_char( false );
|
||||
auto line_comment = proto_comment( );
|
||||
parse_comment_line( stream, line_comment );
|
||||
comment.comments.insert( comment.comments.end( ), line_comment.comments.begin( ),
|
||||
line_comment.comments.end( ) );
|
||||
}
|
||||
stream.consume_space( );
|
||||
}
|
||||
|
||||
void parse_top_level_syntax_body( spb::char_stream & stream, proto_syntax & syntax,
|
||||
proto_comment && comment )
|
||||
{
|
||||
//- syntax = ( "proto2" | "proto3" );
|
||||
|
||||
consume_or_fail( stream, '=' );
|
||||
|
||||
syntax.comments = std::move( comment );
|
||||
if( stream.consume( R"("proto2")" ) )
|
||||
{
|
||||
syntax.version = 2;
|
||||
return consume_statement_end( stream, syntax.comments );
|
||||
}
|
||||
|
||||
if( stream.consume( R"("proto3")" ) )
|
||||
{
|
||||
syntax.version = 3;
|
||||
return consume_statement_end( stream, syntax.comments );
|
||||
}
|
||||
|
||||
stream.throw_parse_error( "expecting proto2 or proto3" );
|
||||
}
|
||||
|
||||
void parse_top_level_syntax_or_service( spb::char_stream & stream, proto_file & file,
|
||||
proto_comment && comment )
|
||||
{
|
||||
if( stream.consume( "syntax" ) )
|
||||
{
|
||||
return parse_top_level_syntax_body( stream, file.syntax, std::move( comment ) );
|
||||
}
|
||||
|
||||
if( stream.consume( "service" ) )
|
||||
{
|
||||
return parse_top_level_service_body( stream, file, std::move( comment ) );
|
||||
}
|
||||
|
||||
stream.throw_parse_error( "expecting syntax or service" );
|
||||
}
|
||||
|
||||
void parse_top_level_import( spb::char_stream & stream, proto_imports & imports,
|
||||
proto_comment && comment )
|
||||
{
|
||||
// "import" [ "weak" | "public" ] strLit ";"
|
||||
consume_or_fail( stream, "import" );
|
||||
stream.consume( "weak" ) || stream.consume( "public" );
|
||||
imports.emplace_back( proto_import{ .file_name = parse_string_literal( stream ),
|
||||
.comments = std::move( comment ) } );
|
||||
consume_statement_end( stream, imports.back( ).comments );
|
||||
}
|
||||
|
||||
void parse_top_level_package( spb::char_stream & stream, proto_base & package,
|
||||
proto_comment && comment )
|
||||
{
|
||||
//- "package" fullIdent ";"
|
||||
consume_or_fail( stream, "package" );
|
||||
package.name = parse_full_ident( stream );
|
||||
package.comment = std::move( comment );
|
||||
consume_statement_end( stream, package.comment );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_option_name( spb::char_stream & stream ) -> std::string_view
|
||||
{
|
||||
auto ident = std::string_view{ };
|
||||
|
||||
//- ( ident | "(" fullIdent ")" ) { "." ident }
|
||||
parse_comment( stream );
|
||||
if( stream.consume( '(' ) )
|
||||
{
|
||||
ident = parse_full_ident( stream );
|
||||
consume_or_fail( stream, ')' );
|
||||
}
|
||||
else
|
||||
{
|
||||
ident = parse_ident( stream );
|
||||
}
|
||||
auto ident2 = std::string_view{ };
|
||||
|
||||
while( stream.consume( '.' ) )
|
||||
{
|
||||
ident2 = parse_ident( stream );
|
||||
}
|
||||
|
||||
if( ident2.empty( ) )
|
||||
{
|
||||
return ident;
|
||||
}
|
||||
|
||||
return { ident.data( ),
|
||||
static_cast< size_t >( ident2.data( ) + ident2.size( ) - ident.data( ) ) };
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_constant( spb::char_stream & stream ) -> std::string_view
|
||||
{
|
||||
//- fullIdent | ( [ "-" | "+" ] intLit ) | ( [ "-" | "+" ] floatLit ) | strLit | boolLit |
|
||||
// MessageValue
|
||||
if( stream.consume( "true" ) )
|
||||
{
|
||||
return "true";
|
||||
}
|
||||
if( stream.consume( "false" ) )
|
||||
{
|
||||
return "false";
|
||||
}
|
||||
const auto c = stream.current_char( );
|
||||
if( c == '"' || c == '\'' )
|
||||
{
|
||||
return parse_string_literal( stream );
|
||||
}
|
||||
if( isdigit( c ) || c == '+' || c == '-' )
|
||||
{
|
||||
return parse_int_or_float( stream );
|
||||
}
|
||||
return parse_full_ident( stream );
|
||||
}
|
||||
|
||||
void parse_option_body( spb::char_stream & stream, proto_options & options )
|
||||
{
|
||||
const auto option_name = parse_option_name( stream );
|
||||
consume_or_fail( stream, '=' );
|
||||
options[ option_name ] = parse_constant( stream );
|
||||
}
|
||||
|
||||
void parse_option_from_comment( const spb::char_stream & stream, proto_options & options,
|
||||
std::string_view comment )
|
||||
{
|
||||
for( ;; )
|
||||
{
|
||||
auto start = comment.find( "[[" );
|
||||
if( start == std::string_view::npos )
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto end = comment.find( "]]", start + 2 );
|
||||
if( end == std::string_view::npos )
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto option = comment.substr( start + 2, end - start - 2 );
|
||||
comment.remove_prefix( end + 2 );
|
||||
auto option_stream = stream;
|
||||
option_stream.skip_to( option.data( ) );
|
||||
parse_option_body( option_stream, options );
|
||||
}
|
||||
}
|
||||
|
||||
void parse_options_from_comments( const spb::char_stream & stream, proto_options & options,
|
||||
const proto_comment & comment )
|
||||
{
|
||||
for( auto & c : comment.comments )
|
||||
{
|
||||
parse_option_from_comment( stream, options, c );
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_option( spb::char_stream & stream, proto_options & options,
|
||||
proto_comment && comment ) -> bool
|
||||
{
|
||||
//- "option" optionName "=" constant ";"
|
||||
if( !stream.consume( "option" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
parse_option_body( stream, options );
|
||||
consume_statement_end( stream, comment );
|
||||
parse_options_from_comments( stream, options, comment );
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse_reserved_names( spb::char_stream & stream, proto_reserved_name & name,
|
||||
proto_comment && comment )
|
||||
{
|
||||
//- strFieldNames = strFieldName { "," strFieldName }
|
||||
//- strFieldName = "'" fieldName "'" | '"' fieldName '"'
|
||||
do
|
||||
{
|
||||
name.insert( parse_string_literal( stream ) );
|
||||
} while( stream.consume( ',' ) );
|
||||
|
||||
consume_statement_end( stream, comment );
|
||||
comment.comments.clear( );
|
||||
}
|
||||
|
||||
void parse_reserved_ranges( spb::char_stream & stream, proto_reserved_range & range,
|
||||
proto_comment && comment )
|
||||
{
|
||||
//- ranges = range { "," range }
|
||||
//- range = intLit [ "to" ( intLit | "max" ) ]
|
||||
do
|
||||
{
|
||||
const auto number = parse_number< uint32_t >( stream );
|
||||
auto number2 = number;
|
||||
|
||||
if( stream.consume( "to" ) )
|
||||
{
|
||||
if( stream.consume( "max" ) )
|
||||
{
|
||||
number2 = std::numeric_limits< decltype( number2 ) >::max( );
|
||||
}
|
||||
else
|
||||
{
|
||||
number2 = parse_number< uint32_t >( stream );
|
||||
}
|
||||
}
|
||||
|
||||
range.emplace_back( number, number2 );
|
||||
} while( stream.consume( ',' ) );
|
||||
|
||||
consume_statement_end( stream, comment );
|
||||
comment.comments.clear( );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_extensions( spb::char_stream & stream, proto_reserved_range & extensions,
|
||||
proto_comment && comment ) -> bool
|
||||
{
|
||||
//- extensions = "extensions" ranges ";"
|
||||
if( !stream.consume( "extensions" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parse_reserved_ranges( stream, extensions, std::move( comment ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_reserved( spb::char_stream & stream, proto_reserved & reserved,
|
||||
proto_comment && comment ) -> bool
|
||||
{
|
||||
//- reserved = "reserved" ( ranges | strFieldNames ) ";"
|
||||
if( !stream.consume( "reserved" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto c = stream.current_char( );
|
||||
if( c == '\'' || c == '"' )
|
||||
{
|
||||
parse_reserved_names( stream, reserved.reserved_name, std::move( comment ) );
|
||||
return true;
|
||||
}
|
||||
parse_reserved_ranges( stream, reserved.reserved_range, std::move( comment ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_field_options( spb::char_stream & stream ) -> proto_options
|
||||
{
|
||||
auto options = proto_options{ };
|
||||
if( stream.consume( '[' ) )
|
||||
{
|
||||
auto first = true;
|
||||
while( !stream.consume( ']' ) )
|
||||
{
|
||||
if( !first )
|
||||
{
|
||||
consume_or_fail( stream, ',' );
|
||||
}
|
||||
parse_option_body( stream, options );
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
void parse_enum_field( spb::char_stream & stream, proto_enum & new_enum, proto_comment && comment )
|
||||
{
|
||||
//- enumField = ident "=" [ "-" ] intLit [ "[" enumValueOption { "," enumValueOption } "]" ]";"
|
||||
//- enumValueOption = optionName "=" constant
|
||||
auto field =
|
||||
proto_base{ .name = parse_ident( stream ),
|
||||
.number = ( consume_or_fail( stream, '=' ),
|
||||
parse_number< decltype( proto_field::number ) >( stream ) ),
|
||||
.options = parse_field_options( stream ),
|
||||
.comment = std::move( comment ) };
|
||||
|
||||
consume_statement_end( stream, field.comment );
|
||||
new_enum.fields.push_back( field );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_enum_body( spb::char_stream & stream, proto_comment && enum_comment )
|
||||
-> proto_enum
|
||||
{
|
||||
//- enumBody = "{" { option | enumField | emptyStatement | reserved } "}"
|
||||
|
||||
auto new_enum = proto_enum{
|
||||
proto_base{
|
||||
.name = parse_ident( stream ),
|
||||
.comment = std::move( enum_comment ),
|
||||
},
|
||||
};
|
||||
consume_or_fail( stream, '{' );
|
||||
|
||||
parse_options_from_comments( stream, new_enum.options, new_enum.comment );
|
||||
|
||||
while( !stream.consume( '}' ) )
|
||||
{
|
||||
auto comment = parse_comment( stream );
|
||||
if( stream.consume( '}' ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( !parse_option( stream, new_enum.options, std::move( comment ) ) &&
|
||||
!parse_reserved( stream, new_enum.reserved, std::move( comment ) ) &&
|
||||
!parse_empty_statement( stream ) )
|
||||
{
|
||||
parse_enum_field( stream, new_enum, std::move( comment ) );
|
||||
}
|
||||
}
|
||||
return new_enum;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_enum( spb::char_stream & stream, proto_enums & enums,
|
||||
proto_comment && comment ) -> bool
|
||||
{
|
||||
//- enum = "enum" enumName enumBody
|
||||
if( !stream.consume( "enum" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
enums.push_back( parse_enum_body( stream, std::move( comment ) ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_field_label( spb::char_stream & stream ) -> proto_field::Label
|
||||
{
|
||||
if( stream.consume( "optional" ) )
|
||||
{
|
||||
return proto_field::Label::OPTIONAL;
|
||||
}
|
||||
if( stream.consume( "repeated" ) )
|
||||
{
|
||||
return proto_field::Label::REPEATED;
|
||||
}
|
||||
if( stream.consume( "required" ) )
|
||||
{
|
||||
return proto_field::Label::NONE;
|
||||
}
|
||||
|
||||
return proto_field::Label::OPTIONAL;
|
||||
// stream.throw_parse_error( "expecting label" );
|
||||
}
|
||||
|
||||
void parse_field( spb::char_stream & stream, proto_fields & fields, proto_comment && comment )
|
||||
{
|
||||
//- field = label type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
|
||||
//- fieldOptions = fieldOption { "," fieldOption }
|
||||
//- fieldOption = optionName "=" constant
|
||||
auto new_field = proto_field{
|
||||
.label = parse_field_label( stream ),
|
||||
.type_name = parse_full_ident( stream ),
|
||||
};
|
||||
|
||||
new_field.name = parse_ident( stream );
|
||||
new_field.number = ( consume_or_fail( stream, '=' ),
|
||||
parse_number< decltype( proto_field::number ) >( stream ) );
|
||||
new_field.options = parse_field_options( stream );
|
||||
new_field.comment = std::move( comment );
|
||||
consume_statement_end( stream, new_field.comment );
|
||||
parse_options_from_comments( stream, new_field.options, new_field.comment );
|
||||
fields.push_back( new_field );
|
||||
}
|
||||
|
||||
//[[nodiscard]] auto parse_extend( spb::char_stream & stream, proto_ast & ) -> bool;
|
||||
//[[nodiscard]] auto parse_extensions( spb::char_stream & stream, proto_fields & ) -> bool;
|
||||
//[[nodiscard]] auto parse_oneof( spb::char_stream & stream, proto_ast & ) -> bool;
|
||||
|
||||
auto parse_map_key_type( spb::char_stream & stream ) -> std::string_view
|
||||
{
|
||||
//- keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" | "fixed32" |
|
||||
//"fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"
|
||||
constexpr auto key_types =
|
||||
std::array< std::string_view, 12 >{ { "int32", "int64", "uint32", "uint64", "sint32",
|
||||
"sint64", "fixed32", "fixed64", "sfixed32",
|
||||
"sfixed64", "bool", "string" } };
|
||||
for( auto key_type : key_types )
|
||||
{
|
||||
if( stream.consume( key_type ) )
|
||||
{
|
||||
return key_type;
|
||||
}
|
||||
}
|
||||
stream.throw_parse_error( "expecting map key type" );
|
||||
}
|
||||
|
||||
auto parse_map_body( spb::char_stream & stream, proto_comment && comment ) -> proto_map
|
||||
{
|
||||
//- "map" "<" keyType "," type ">" mapName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
|
||||
|
||||
auto new_map = proto_map{
|
||||
.key = proto_field{ .type_name = parse_map_key_type( stream ) },
|
||||
.value = proto_field{ .type_name =
|
||||
( consume_or_fail( stream, ',' ), parse_full_ident( stream ) ) },
|
||||
};
|
||||
new_map.name = ( consume_or_fail( stream, '>' ), parse_ident( stream ) );
|
||||
new_map.number =
|
||||
( consume_or_fail( stream, '=' ), parse_number< decltype( proto_map::number ) >( stream ) );
|
||||
new_map.options = parse_field_options( stream );
|
||||
new_map.comment = std::move( comment );
|
||||
consume_statement_end( stream, new_map.comment );
|
||||
return new_map;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_map_field( spb::char_stream & stream, proto_maps & maps,
|
||||
proto_comment && comment ) -> bool
|
||||
{
|
||||
//- "map" "<"
|
||||
if( !stream.consume( "map" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
consume_or_fail( stream, '<' );
|
||||
maps.push_back( parse_map_body( stream, std::move( comment ) ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse_oneof_field( spb::char_stream & stream, proto_fields & fields, proto_comment && comment )
|
||||
{
|
||||
//- oneofField = type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
|
||||
auto new_field = proto_field{ .type_name = parse_full_ident( stream ) };
|
||||
|
||||
new_field.name = parse_ident( stream );
|
||||
new_field.number = ( consume_or_fail( stream, '=' ),
|
||||
parse_number< decltype( proto_field::number ) >( stream ) );
|
||||
new_field.options = parse_field_options( stream );
|
||||
new_field.comment = std::move( comment );
|
||||
consume_statement_end( stream, new_field.comment );
|
||||
fields.push_back( new_field );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_oneof_body( spb::char_stream & stream, proto_comment && oneof_comment )
|
||||
-> proto_oneof
|
||||
{
|
||||
//- oneof = "oneof" oneofName "{" { option | oneofField } "}"
|
||||
auto new_oneof = proto_oneof{ proto_base{
|
||||
.name = parse_ident( stream ),
|
||||
.comment = std::move( oneof_comment ),
|
||||
} };
|
||||
consume_or_fail( stream, '{' );
|
||||
while( !stream.consume( '}' ) )
|
||||
{
|
||||
auto comment = parse_comment( stream );
|
||||
if( stream.consume( '}' ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( !parse_option( stream, new_oneof.options, std::move( comment ) ) )
|
||||
{
|
||||
parse_oneof_field( stream, new_oneof.fields, std::move( comment ) );
|
||||
}
|
||||
}
|
||||
return new_oneof;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_oneof( spb::char_stream & stream, proto_oneofs & oneofs,
|
||||
proto_comment && comment ) -> bool
|
||||
{
|
||||
//- oneof = "oneof" oneofName "{" { option | oneofField } "}"
|
||||
if( !stream.consume( "oneof" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
oneofs.push_back( parse_oneof_body( stream, std::move( comment ) ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_message( spb::char_stream & stream, proto_messages & messages,
|
||||
proto_comment && comment ) -> bool;
|
||||
|
||||
void parse_message_body( spb::char_stream & stream, proto_messages & messages,
|
||||
proto_comment && message_comment )
|
||||
{
|
||||
//- messageBody = messageName "{" { field | enum | message | extend | extensions | group |
|
||||
// option | oneof | mapField | reserved | emptyStatement } "}"
|
||||
auto new_message = proto_message{ proto_base{
|
||||
.name = parse_ident( stream ),
|
||||
.comment = std::move( message_comment ),
|
||||
} };
|
||||
|
||||
consume_or_fail( stream, '{' );
|
||||
parse_options_from_comments( stream, new_message.options, new_message.comment );
|
||||
|
||||
while( !stream.consume( '}' ) )
|
||||
{
|
||||
auto comment = parse_comment( stream );
|
||||
if( stream.consume( '}' ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( !parse_empty_statement( stream ) &&
|
||||
!parse_enum( stream, new_message.enums, std::move( comment ) ) &&
|
||||
!parse_message( stream, new_message.messages, std::move( comment ) ) &&
|
||||
//! parse_extend( stream, new_message.extends ) &&
|
||||
!parse_extensions( stream, new_message.extensions, std::move( comment ) ) &&
|
||||
!parse_oneof( stream, new_message.oneofs, std::move( comment ) ) &&
|
||||
!parse_map_field( stream, new_message.maps, std::move( comment ) ) &&
|
||||
!parse_reserved( stream, new_message.reserved, std::move( comment ) ) &&
|
||||
!parse_option( stream, new_message.options, std::move( comment ) ) )
|
||||
{
|
||||
parse_field( stream, new_message.fields, std::move( comment ) );
|
||||
}
|
||||
}
|
||||
messages.push_back( new_message );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_message( spb::char_stream & stream, proto_messages & messages,
|
||||
proto_comment && comment ) -> bool
|
||||
{
|
||||
//- "message" messageName messageBody
|
||||
if( !stream.consume( "message" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
parse_message_body( stream, messages, std::move( comment ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse_top_level_option( spb::char_stream & stream, proto_options & options,
|
||||
proto_comment && comment )
|
||||
{
|
||||
parse_or_throw( parse_option( stream, options, std::move( comment ) ), stream,
|
||||
"expecting option" );
|
||||
}
|
||||
|
||||
void parse_top_level_message( spb::char_stream & stream, proto_messages & messages,
|
||||
proto_comment && comment )
|
||||
{
|
||||
parse_or_throw( parse_message( stream, messages, std::move( comment ) ), stream,
|
||||
"expecting message" );
|
||||
}
|
||||
|
||||
void parse_top_level_enum( spb::char_stream & stream, proto_enums & enums,
|
||||
proto_comment && comment )
|
||||
{
|
||||
parse_or_throw( parse_enum( stream, enums, std::move( comment ) ), stream, "expecting enum" );
|
||||
}
|
||||
|
||||
void parse_top_level( spb::char_stream & stream, proto_file & file, proto_comment && comment )
|
||||
{
|
||||
switch( stream.current_char( ) )
|
||||
{
|
||||
case '\0':
|
||||
return;
|
||||
case 's':
|
||||
return parse_top_level_syntax_or_service( stream, file, std::move( comment ) );
|
||||
case 'i':
|
||||
return parse_top_level_import( stream, file.imports, std::move( comment ) );
|
||||
case 'p':
|
||||
return parse_top_level_package( stream, file.package, std::move( comment ) );
|
||||
case 'o':
|
||||
return parse_top_level_option( stream, file.options, std::move( comment ) );
|
||||
case 'm':
|
||||
return parse_top_level_message( stream, file.package.messages, std::move( comment ) );
|
||||
case 'e':
|
||||
return parse_top_level_enum( stream, file.package.enums, std::move( comment ) );
|
||||
case ';':
|
||||
return ( void ) parse_empty_statement( stream );
|
||||
|
||||
default:
|
||||
return stream.throw_parse_error( "expecting top level definition" );
|
||||
}
|
||||
}
|
||||
|
||||
void set_default_options( proto_file & file )
|
||||
{
|
||||
file.options[ option_optional_type ] = "std::optional<$>";
|
||||
file.options[ option_optional_include ] = "<optional>";
|
||||
|
||||
file.options[ option_repeated_type ] = "std::vector<$>";
|
||||
file.options[ option_repeated_include ] = "<vector>";
|
||||
|
||||
file.options[ option_string_type ] = "std::string";
|
||||
file.options[ option_string_include ] = "<string>";
|
||||
|
||||
file.options[ option_bytes_type ] = "std::vector<$>";
|
||||
file.options[ option_bytes_include ] = "<vector>";
|
||||
|
||||
file.options[ option_pointer_type ] = "std::unique_ptr<$>";
|
||||
file.options[ option_pointer_include ] = "<memory>";
|
||||
|
||||
file.options[ option_enum_type ] = "int32";
|
||||
}
|
||||
|
||||
[[nodiscard]] auto parse_proto_file( fs::path file, parsed_files & already_parsed,
|
||||
std::span< const fs::path > import_paths,
|
||||
const fs::path & base_dir ) -> proto_file
|
||||
{
|
||||
try
|
||||
{
|
||||
file = find_file_in_paths( file, import_paths, base_dir );
|
||||
|
||||
auto result = proto_file{
|
||||
.path = file,
|
||||
.content = load_file( file ),
|
||||
};
|
||||
|
||||
parse_proto_file_content( result );
|
||||
already_parsed.insert( file.string( ) );
|
||||
result.file_imports =
|
||||
parse_all_imports( result, already_parsed, import_paths, file.parent_path( ) );
|
||||
resolve_messages( result );
|
||||
return result;
|
||||
}
|
||||
catch( const std::exception & e )
|
||||
{
|
||||
throw std::runtime_error( file.string( ) + ":" + e.what( ) );
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
void parse_proto_file_content( proto_file & file )
|
||||
{
|
||||
set_default_options( file );
|
||||
|
||||
auto stream = spb::char_stream( file.content );
|
||||
|
||||
while( !stream.empty( ) )
|
||||
{
|
||||
auto comment = parse_comment( stream );
|
||||
parse_options_from_comments( stream, file.options, comment );
|
||||
parse_top_level( stream, file, std::move( comment ) );
|
||||
}
|
||||
}
|
||||
|
||||
auto parse_proto_file( const fs::path & file, std::span< const fs::path > import_paths,
|
||||
const fs::path & base_dir ) -> proto_file
|
||||
{
|
||||
auto already_parsed = parsed_files( );
|
||||
return parse_proto_file( file, already_parsed, import_paths, base_dir );
|
||||
}
|
||||
|
||||
[[nodiscard]] auto cpp_file_name_from_proto( const fs::path & proto_file_path,
|
||||
std::string_view extension ) -> fs::path
|
||||
{
|
||||
return proto_file_path.stem( ).concat( extension );
|
||||
}
|
||||
46
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/parser.h
vendored
Normal file
46
3rdparty/simple-protobuf/src/spb-proto-compiler/parser/parser.h
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/***************************************************************************\
|
||||
* Name : .proto parser *
|
||||
* Description : parse proto file and constructs an ast tree *
|
||||
* Author : antonin.kriz@gmail.com *
|
||||
* ------------------------------------------------------------------------- *
|
||||
* This is free software; you can redistribute it and/or modify it under the *
|
||||
* terms of the MIT license. A copy of the license can be found in the file *
|
||||
* "LICENSE" at the root of this distribution. *
|
||||
\***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/proto-file.h"
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* @brief generate C++ file name from a .proto file name
|
||||
* example: "foo.proto" -> "foo.pb.h"
|
||||
*
|
||||
* @param proto_file_path proto file name
|
||||
* @param extension CPP file extension (example: ".pb.h")
|
||||
* @return CPP file name
|
||||
*/
|
||||
[[nodiscard]] auto cpp_file_name_from_proto( const std::filesystem::path & proto_file_path,
|
||||
std::string_view extension ) -> std::filesystem::path;
|
||||
|
||||
/**
|
||||
* @brief parse proto file
|
||||
*
|
||||
* @param file_path file path
|
||||
* @param import_paths proto import paths (searched in order)
|
||||
* @param base_dir working directory
|
||||
* @return proto_file parsed proto
|
||||
*/
|
||||
auto parse_proto_file(
|
||||
const std::filesystem::path & file_path, std::span< const std::filesystem::path > import_paths,
|
||||
const std::filesystem::path & base_dir = std::filesystem::current_path( ) ) -> proto_file;
|
||||
|
||||
/**
|
||||
* @brief parse proto file content, used for fuzzing
|
||||
*
|
||||
* @param file proto file
|
||||
*/
|
||||
void parse_proto_file_content( proto_file & file );
|
||||
195
3rdparty/simple-protobuf/test/CMakeLists.txt
vendored
Normal file
195
3rdparty/simple-protobuf/test/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
include(cmake/doctest.cmake)
|
||||
|
||||
FILE(GLOB protos ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto)
|
||||
|
||||
SET(PB_PACKAGE "")
|
||||
configure_file(proto/scalar/scalar.proto.in ${CMAKE_CURRENT_BINARY_DIR}/scalar.proto @ONLY)
|
||||
configure_file(proto/person/person.proto.in ${CMAKE_CURRENT_BINARY_DIR}/person.proto @ONLY)
|
||||
configure_file(proto/name/name.proto.in ${CMAKE_CURRENT_BINARY_DIR}/name.proto @ONLY)
|
||||
|
||||
if(SPB_PROTO_BUILD_ETL_TESTS)
|
||||
configure_file(proto/etl/etl-scalar.proto.in ${CMAKE_CURRENT_BINARY_DIR}/etl-scalar.proto @ONLY)
|
||||
configure_file(proto/etl/etl-person.proto.in ${CMAKE_CURRENT_BINARY_DIR}/etl-person.proto @ONLY)
|
||||
endif()
|
||||
|
||||
SET(PB_PACKAGE ".gpb")
|
||||
configure_file(proto/scalar/scalar.proto.in ${CMAKE_CURRENT_BINARY_DIR}/gpb-scalar.proto @ONLY)
|
||||
configure_file(proto/person/person.proto.in ${CMAKE_CURRENT_BINARY_DIR}/gpb-person.proto @ONLY)
|
||||
configure_file(proto/name/name.proto.in ${CMAKE_CURRENT_BINARY_DIR}/gpb-name.proto @ONLY)
|
||||
if(SPB_PROTO_BUILD_ETL_TESTS)
|
||||
configure_file(proto/etl/etl-scalar.proto.in ${CMAKE_CURRENT_BINARY_DIR}/gpb-etl-scalar.proto @ONLY)
|
||||
configure_file(proto/etl/etl-person.proto.in ${CMAKE_CURRENT_BINARY_DIR}/gpb-etl-person.proto @ONLY)
|
||||
endif()
|
||||
|
||||
spb_protobuf_generate(SPB_PROTO_SRCS SPB_PROTO_HDRS ${protos})
|
||||
spb_protobuf_generate(SPB_PROTO_PERSON_SRC SPB_PROTO_PERSON_HDR ${CMAKE_CURRENT_BINARY_DIR}/person.proto)
|
||||
spb_protobuf_generate(SPB_PROTO_NAME_SRC SPB_PROTO_NAME_HDR ${CMAKE_CURRENT_BINARY_DIR}/name.proto)
|
||||
spb_protobuf_generate(SPB_PROTO_SCALAR_SRC SPB_PROTO_SCALAR_HDR ${CMAKE_CURRENT_BINARY_DIR}/scalar.proto)
|
||||
if(SPB_PROTO_BUILD_ETL_TESTS)
|
||||
spb_protobuf_generate(SPB_PROTO_ETL_SCALAR_SRC SPB_PROTO_ETL_HDR ${CMAKE_CURRENT_BINARY_DIR}/etl-scalar.proto)
|
||||
spb_protobuf_generate(SPB_PROTO_ETL_PERSON_SRC SPB_PROTO_ETL_HDR ${CMAKE_CURRENT_BINARY_DIR}/etl-person.proto)
|
||||
endif()
|
||||
|
||||
add_custom_target(unit_tests)
|
||||
|
||||
add_executable(base64-test base64.cpp)
|
||||
spb_set_compile_options(base64-test)
|
||||
spb_disable_warnings(base64-test)
|
||||
target_link_libraries(base64-test PUBLIC spb-proto)
|
||||
add_dependencies(unit_tests base64-test)
|
||||
doctest_discover_tests(base64-test)
|
||||
|
||||
add_executable(json-test json.cpp ${SPB_PROTO_PERSON_SRC} ${SPB_PROTO_NAME_SRC} ${SPB_PROTO_SCALAR_SRC})
|
||||
spb_set_compile_options(json-test)
|
||||
spb_disable_warnings(json-test)
|
||||
target_link_libraries(json-test PUBLIC spb-proto)
|
||||
add_dependencies(unit_tests json-test)
|
||||
doctest_discover_tests(json-test)
|
||||
|
||||
add_executable(pb-test pb.cpp ${SPB_PROTO_SRCS} ${SPB_PROTO_PERSON_SRC} ${SPB_PROTO_NAME_SRC} ${SPB_PROTO_SCALAR_SRC})
|
||||
spb_set_compile_options(pb-test)
|
||||
spb_disable_warnings(pb-test)
|
||||
target_link_libraries(pb-test PUBLIC spb-proto)
|
||||
add_dependencies(unit_tests pb-test)
|
||||
doctest_discover_tests(pb-test)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT IPO_ENABLED)
|
||||
endif()
|
||||
|
||||
add_executable(compiled-protos protos.cpp ${SPB_PROTO_SRCS} ${SPB_PROTO_HDRS})
|
||||
spb_set_compile_options(compiled-protos)
|
||||
spb_disable_warnings(compiled-protos)
|
||||
target_link_libraries(compiled-protos PUBLIC spb-proto)
|
||||
|
||||
if(SPB_PROTO_BUILD_COMPATIBILITY_TESTS)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND EXISTS "/etc/arch-release")
|
||||
set(ABSL_PROPAGATE_CXX_STD On)
|
||||
set(ABSL_ENABLE_INSTALL Off)
|
||||
set(BUILD_SHARED_LIBS Off)
|
||||
|
||||
set(protobuf_INSTALL Off)
|
||||
set(protobuf_BUILD_LIBPROTOC Off)
|
||||
set(protobuf_BUILD_TESTS Off)
|
||||
set(protobuf_BUILD_LIBUPB Off)
|
||||
set(protobuf_BUILD_SHARED_LIBS Off)
|
||||
set(protobuf_BUILD_LIBUPB Off)
|
||||
set(protobuf_ABSL_PROVIDER "module")
|
||||
|
||||
FetchContent_Declare(
|
||||
protobuf
|
||||
GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git
|
||||
GIT_TAG v30.0
|
||||
)
|
||||
FetchContent_MakeAvailable(protobuf)
|
||||
else()
|
||||
find_package(Protobuf REQUIRED)
|
||||
endif()
|
||||
|
||||
function(PROTOBUF_GENERATE_CPP SRC_VAR HDR_VAR PROTO_FILE)
|
||||
get_filename_component(PROTO_NAME ${PROTO_FILE} NAME_WE)
|
||||
set(OUTPUT_CC "${CMAKE_CURRENT_BINARY_DIR}/${PROTO_NAME}.pb.cc")
|
||||
set(OUTPUT_H "${CMAKE_CURRENT_BINARY_DIR}/${PROTO_NAME}.pb.h")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_CC} ${OUTPUT_H}
|
||||
COMMAND protobuf::protoc
|
||||
ARGS --cpp_out=${CMAKE_CURRENT_BINARY_DIR} -I${CMAKE_CURRENT_BINARY_DIR} ${PROTO_FILE}
|
||||
DEPENDS ${PROTO_FILE}
|
||||
COMMENT "Generating Protobuf files for ${PROTO_FILE} as ${OUTPUT_CC} and ${OUTPUT_H}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
set(${SRC_VAR} ${OUTPUT_CC} PARENT_SCOPE)
|
||||
set(${HDR_VAR} ${OUTPUT_H} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
PROTOBUF_GENERATE_CPP(PROTO_PERSON_SRC PROTO_PERSON_HDR ${CMAKE_CURRENT_BINARY_DIR}/gpb-person.proto)
|
||||
PROTOBUF_GENERATE_CPP(PROTO_NAME_SRC PROTO_NAME_HDR ${CMAKE_CURRENT_BINARY_DIR}/gpb-name.proto)
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SCALAR_SRC PROTO_SCALAR_HDR ${CMAKE_CURRENT_BINARY_DIR}/gpb-scalar.proto)
|
||||
if(SPB_PROTO_BUILD_ETL_TESTS)
|
||||
PROTOBUF_GENERATE_CPP(PROTO_ETL_SCALAR_SRC PROTO_ETL_HDR ${CMAKE_CURRENT_BINARY_DIR}/gpb-etl-scalar.proto)
|
||||
PROTOBUF_GENERATE_CPP(PROTO_ETL_PERSON_SRC PROTO_ETL_HDR ${CMAKE_CURRENT_BINARY_DIR}/gpb-etl-person.proto)
|
||||
endif()
|
||||
|
||||
add_library(gpb-proto STATIC ${PROTO_ETL_PERSON_SRC} ${PROTO_ETL_SCALAR_SRC} ${PROTO_SCALAR_SRC} ${PROTO_PERSON_SRC} ${PROTO_NAME_SRC})
|
||||
target_link_libraries(gpb-proto PUBLIC protobuf::libprotobuf)
|
||||
target_compile_features(gpb-proto INTERFACE cxx_std_20)
|
||||
|
||||
add_executable(gpb-compatibility-test gpb-compatibility.cpp ${SPB_PROTO_PERSON_SRC} ${SPB_PROTO_NAME_SRC} ${SPB_PROTO_SCALAR_SRC})
|
||||
spb_set_compile_options(gpb-compatibility-test)
|
||||
target_link_libraries(gpb-compatibility-test PUBLIC spb-proto gpb-proto)
|
||||
add_dependencies(unit_tests gpb-compatibility-test)
|
||||
doctest_discover_tests(gpb-compatibility-test)
|
||||
|
||||
if(SPB_PROTO_BUILD_ETL_TESTS)
|
||||
Include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
etl
|
||||
GIT_REPOSITORY https://github.com/ETLCPP/etl
|
||||
GIT_TAG "20.39.4"
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(etl)
|
||||
|
||||
add_executable(etl-compatibility-test etl-compatibility.cpp ${SPB_PROTO_ETL_SCALAR_SRC} ${SPB_PROTO_ETL_PERSON_SRC})
|
||||
spb_enable_warnings(etl-compatibility-test)
|
||||
target_link_libraries(etl-compatibility-test PUBLIC spb-proto gpb-proto etl::etl)
|
||||
add_dependencies(unit_tests etl-compatibility-test)
|
||||
doctest_discover_tests(etl-compatibility-test)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SPB_PROTO_BUILD_COMPILER_TESTS)
|
||||
add_executable(spb-protoc-test parser.cpp
|
||||
../../src/spb-proto-compiler/parser/parser.cpp
|
||||
../../src/spb-proto-compiler/dumper/pb/dumper.cpp
|
||||
../../src/spb-proto-compiler/dumper/json/dumper.cpp
|
||||
../../src/spb-proto-compiler/dumper/dumper.cpp
|
||||
../../src/spb-proto-compiler/dumper/header.cpp
|
||||
../../src/spb-proto-compiler/io/file.cpp
|
||||
../../src/spb-proto-compiler/ast/ast.cpp
|
||||
../../src/spb-proto-compiler/ast/ast-types.cpp
|
||||
../../src/spb-proto-compiler/ast/ast-messages-order.cpp
|
||||
)
|
||||
target_include_directories(spb-protoc-test PUBLIC ${CMAKE_SOURCE_DIR}/src/spb-proto-compiler)
|
||||
target_link_libraries(spb-protoc-test PUBLIC spb-proto)
|
||||
add_dependencies(unit_tests spb-protoc-test)
|
||||
doctest_discover_tests(spb-protoc-test)
|
||||
endif()
|
||||
|
||||
if(SPB_PROTO_BUILD_FUZZER_TESTS)
|
||||
if(SPB_PROTO_USE_ADDRESS_SANITIZER)
|
||||
add_compile_options("-fsanitize=fuzzer,address" -O1 -g)
|
||||
add_link_options("-fsanitize=fuzzer,address")
|
||||
elseif(SPB_PROTO_USE_UB_SANITIZER)
|
||||
message("-- Enable undefined behavior sanitizer" -O1 -g)
|
||||
add_compile_options("-fsanitize=fuzzer,undefined")
|
||||
add_link_options("-fsanitize=fuzzer,undefined")
|
||||
endif()
|
||||
|
||||
add_executable(spb-protoc-fuzzer fuzzer/spb-protoc-fuzzer.cpp
|
||||
../../src/spb-proto-compiler/parser/parser.cpp
|
||||
../../src/spb-proto-compiler/io/file.cpp
|
||||
../../src/spb-proto-compiler/ast/ast.cpp
|
||||
)
|
||||
target_include_directories(spb-protoc-fuzzer PUBLIC ${CMAKE_SOURCE_DIR}/src/spb-proto-compiler)
|
||||
target_link_libraries(spb-protoc-fuzzer PUBLIC spb-proto)
|
||||
spb_disable_warnings(spb-protoc-fuzzer)
|
||||
|
||||
add_executable(spb-pb-fuzzer fuzzer/spb-pb-fuzzer.cpp
|
||||
${SPB_PROTO_PERSON_SRC} ${SPB_PROTO_NAME_SRC} ${SPB_PROTO_SCALAR_SRC} ${SPB_PROTO_SRCS}
|
||||
)
|
||||
target_link_libraries(spb-pb-fuzzer PUBLIC spb-proto)
|
||||
spb_disable_warnings(spb-pb-fuzzer)
|
||||
|
||||
add_executable(spb-json-fuzzer fuzzer/spb-json-fuzzer.cpp
|
||||
${SPB_PROTO_PERSON_SRC} ${SPB_PROTO_NAME_SRC} ${SPB_PROTO_SCALAR_SRC} ${SPB_PROTO_SRCS}
|
||||
)
|
||||
target_link_libraries(spb-json-fuzzer PUBLIC spb-proto)
|
||||
spb_disable_warnings(spb-json-fuzzer)
|
||||
endif()
|
||||
|
||||
|
||||
155
3rdparty/simple-protobuf/test/base64.cpp
vendored
Normal file
155
3rdparty/simple-protobuf/test/base64.cpp
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <spb/json/deserialize.hpp>
|
||||
#include <spb/json/serialize.hpp>
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest.h"
|
||||
#include <spb/json/base64.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
auto base64_encode_size( std::string_view value ) -> size_t
|
||||
{
|
||||
auto stream = spb::json::detail::ostream( nullptr );
|
||||
spb::json::detail::base64_encode( stream, { reinterpret_cast< const std::byte * >( value.data( ) ), value.size( ) } );
|
||||
return stream.size( );
|
||||
}
|
||||
|
||||
auto base64_encode( std::string_view value ) -> std::string
|
||||
{
|
||||
auto encode_size = base64_encode_size( value );
|
||||
auto result = std::string( encode_size, '\0' );
|
||||
auto writer = [ ptr = result.data( ) ]( const void * data, size_t size ) mutable
|
||||
{
|
||||
memcpy( ptr, data, size );
|
||||
ptr += size;
|
||||
};
|
||||
auto stream = spb::json::detail::ostream( writer );
|
||||
spb::json::detail::base64_encode( stream, { reinterpret_cast< const std::byte * >( value.data( ) ), value.size( ) } );
|
||||
return result;
|
||||
}
|
||||
|
||||
auto generate_random_bytes( size_t size ) -> std::string
|
||||
{
|
||||
auto result = std::string( size, 0 );
|
||||
for( auto & c : result )
|
||||
{
|
||||
c = ( char ) rand( );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto base64_decode( std::string_view value ) -> std::string
|
||||
{
|
||||
auto reader = [ ptr = value.data( ), end = value.data( ) + value.size( ) ]( void * data, size_t size ) mutable -> size_t
|
||||
{
|
||||
size_t bytes_left = end - ptr;
|
||||
|
||||
size = std::min( size, bytes_left );
|
||||
memcpy( data, ptr, size );
|
||||
ptr += size;
|
||||
return size;
|
||||
};
|
||||
|
||||
auto stream = spb::json::detail::istream( reader );
|
||||
auto result = std::vector< std::byte >( );
|
||||
|
||||
spb::json::detail::base64_decode_string( result, stream );
|
||||
REQUIRE_THROWS( ( void ) stream.view( 1 ) );
|
||||
|
||||
return { std::string( reinterpret_cast< const char * >( result.data( ) ), reinterpret_cast< const char * >( result.data( ) + result.size( ) ) ) };
|
||||
}
|
||||
|
||||
template < size_t N >
|
||||
auto base64_decode_fixed( std::string_view value ) -> std::string
|
||||
{
|
||||
auto reader = [ ptr = value.data( ), end = value.data( ) + value.size( ) ]( void * data, size_t size ) mutable -> size_t
|
||||
{
|
||||
size_t bytes_left = end - ptr;
|
||||
|
||||
size = std::min( size, bytes_left );
|
||||
memcpy( data, ptr, size );
|
||||
ptr += size;
|
||||
return size;
|
||||
};
|
||||
|
||||
auto stream = spb::json::detail::istream( reader );
|
||||
auto result = std::array< std::byte, N >( );
|
||||
|
||||
spb::json::detail::base64_decode_string( result, stream );
|
||||
REQUIRE_THROWS( ( void ) stream.view( 1 ) );
|
||||
|
||||
return { std::string( reinterpret_cast< const char * >( result.data( ) ), reinterpret_cast< const char * >( result.data( ) + result.size( ) ) ) };
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
TEST_CASE( "base64" )
|
||||
{
|
||||
SUBCASE( "base64_encode" )
|
||||
{
|
||||
CHECK( base64_encode( "hello world" ) == "aGVsbG8gd29ybGQ=" );
|
||||
|
||||
CHECK( base64_encode( "" ) == "" );
|
||||
CHECK( base64_encode( "f" ) == "Zg==" );
|
||||
CHECK( base64_encode( "fo" ) == "Zm8=" );
|
||||
CHECK( base64_encode( "foo" ) == "Zm9v" );
|
||||
CHECK( base64_encode( "foob" ) == "Zm9vYg==" );
|
||||
CHECK( base64_encode( "fooba" ) == "Zm9vYmE=" );
|
||||
CHECK( base64_encode( "foobar" ) == "Zm9vYmFy" );
|
||||
}
|
||||
SUBCASE( "base64_decode" )
|
||||
{
|
||||
SUBCASE( "invalid" )
|
||||
{
|
||||
CHECK_THROWS( base64_decode( R"(")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("Zg=")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("Zg")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("Z")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("Zg==)" ) );
|
||||
}
|
||||
|
||||
CHECK( base64_decode( R"("")" ) == "" );
|
||||
CHECK( base64_decode( R"("Zg==")" ) == "f" );
|
||||
CHECK( base64_decode( R"("Zm8=")" ) == "fo" );
|
||||
CHECK( base64_decode( R"("Zm9v")" ) == "foo" );
|
||||
CHECK( base64_decode( R"("Zm9vYg==")" ) == "foob" );
|
||||
CHECK( base64_decode( R"("Zm9vYmE=")" ) == "fooba" );
|
||||
CHECK( base64_decode( R"("Zm9vYmFy")" ) == "foobar" );
|
||||
CHECK( base64_decode_fixed< 6 >( R"("Zm9vYmFy")" ) == "foobar" );
|
||||
CHECK_THROWS( base64_decode_fixed< 5 >( R"("Zm9vYmFy")" ) );
|
||||
CHECK_THROWS( base64_decode_fixed< 7 >( R"("Zm9vYmFy")" ) );
|
||||
CHECK_THROWS( base64_decode_fixed< 3 >( R"("Zm9vYmFy")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("Zm9vY!Fy")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("Zm9vY^Fy")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("!m9vYmFy")" ) );
|
||||
CHECK_THROWS( base64_decode( R"("&m9vYmFy")" ) );
|
||||
}
|
||||
SUBCASE( "encode/decode" )
|
||||
{
|
||||
const auto buffer_max_size = SPB_READ_BUFFER_SIZE * 10;
|
||||
for( auto i = 8U; i <= buffer_max_size; i++ )
|
||||
{
|
||||
srand( i );
|
||||
|
||||
auto bytes = generate_random_bytes( i );
|
||||
auto encoded = base64_encode( bytes );
|
||||
CHECK( ( encoded.size( ) % 4 ) == 0 );
|
||||
CHECK( std::all_of( encoded.begin( ), encoded.end( ), isprint ) );
|
||||
{
|
||||
auto decoded = base64_decode( '"' + encoded + '"' );
|
||||
CHECK( decoded == bytes );
|
||||
}
|
||||
if( i == 8 )
|
||||
{
|
||||
auto decoded = base64_decode_fixed< 8 >( '"' + encoded + '"' );
|
||||
CHECK( decoded == bytes );
|
||||
}
|
||||
if( i == buffer_max_size )
|
||||
{
|
||||
auto decoded = base64_decode_fixed< buffer_max_size >( '"' + encoded + '"' );
|
||||
CHECK( decoded == bytes );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
189
3rdparty/simple-protobuf/test/cmake/doctest.cmake
vendored
Normal file
189
3rdparty/simple-protobuf/test/cmake/doctest.cmake
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
doctest
|
||||
-----
|
||||
|
||||
This module defines a function to help use the doctest test framework.
|
||||
|
||||
The :command:`doctest_discover_tests` discovers tests by asking the compiled test
|
||||
executable to enumerate its tests. This does not require CMake to be re-run
|
||||
when tests change. However, it may not work in a cross-compiling environment,
|
||||
and setting test properties is less convenient.
|
||||
|
||||
This command is intended to replace use of :command:`add_test` to register
|
||||
tests, and will create a separate CTest test for each doctest test case. Note
|
||||
that this is in some cases less efficient, as common set-up and tear-down logic
|
||||
cannot be shared by multiple test cases executing in the same instance.
|
||||
However, it provides more fine-grained pass/fail information to CTest, which is
|
||||
usually considered as more beneficial. By default, the CTest test name is the
|
||||
same as the doctest name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
|
||||
|
||||
.. command:: doctest_discover_tests
|
||||
|
||||
Automatically add tests with CTest by querying the compiled test executable
|
||||
for available tests::
|
||||
|
||||
doctest_discover_tests(target
|
||||
[TEST_SPEC arg1...]
|
||||
[EXTRA_ARGS arg1...]
|
||||
[WORKING_DIRECTORY dir]
|
||||
[TEST_PREFIX prefix]
|
||||
[TEST_SUFFIX suffix]
|
||||
[PROPERTIES name1 value1...]
|
||||
[ADD_LABELS value]
|
||||
[TEST_LIST var]
|
||||
[JUNIT_OUTPUT_DIR dir]
|
||||
)
|
||||
|
||||
``doctest_discover_tests`` sets up a post-build command on the test executable
|
||||
that generates the list of tests by parsing the output from running the test
|
||||
with the ``--list-test-cases`` argument. This ensures that the full
|
||||
list of tests is obtained. Since test discovery occurs at build time, it is
|
||||
not necessary to re-run CMake when the list of tests changes.
|
||||
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
|
||||
in order to function in a cross-compiling environment.
|
||||
|
||||
Additionally, setting properties on tests is somewhat less convenient, since
|
||||
the tests are not available at CMake time. Additional test properties may be
|
||||
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
|
||||
more fine-grained test control is needed, custom content may be provided
|
||||
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
|
||||
directory property. The set of discovered tests is made accessible to such a
|
||||
script via the ``<target>_TESTS`` variable.
|
||||
|
||||
The options are:
|
||||
|
||||
``target``
|
||||
Specifies the doctest executable, which must be a known CMake executable
|
||||
target. CMake will substitute the location of the built executable when
|
||||
running the test.
|
||||
|
||||
``TEST_SPEC arg1...``
|
||||
Specifies test cases, wildcarded test cases, tags and tag expressions to
|
||||
pass to the doctest executable with the ``--list-test-cases`` argument.
|
||||
|
||||
``EXTRA_ARGS arg1...``
|
||||
Any extra arguments to pass on the command line to each test case.
|
||||
|
||||
``WORKING_DIRECTORY dir``
|
||||
Specifies the directory in which to run the discovered test cases. If this
|
||||
option is not provided, the current binary directory is used.
|
||||
|
||||
``TEST_PREFIX prefix``
|
||||
Specifies a ``prefix`` to be prepended to the name of each discovered test
|
||||
case. This can be useful when the same test executable is being used in
|
||||
multiple calls to ``doctest_discover_tests()`` but with different
|
||||
``TEST_SPEC`` or ``EXTRA_ARGS``.
|
||||
|
||||
``TEST_SUFFIX suffix``
|
||||
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
|
||||
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
|
||||
be specified.
|
||||
|
||||
``PROPERTIES name1 value1...``
|
||||
Specifies additional properties to be set on all tests discovered by this
|
||||
invocation of ``doctest_discover_tests``.
|
||||
|
||||
``ADD_LABELS value``
|
||||
Specifies if the test labels should be set automatically.
|
||||
|
||||
``TEST_LIST var``
|
||||
Make the list of tests available in the variable ``var``, rather than the
|
||||
default ``<target>_TESTS``. This can be useful when the same test
|
||||
executable is being used in multiple calls to ``doctest_discover_tests()``.
|
||||
Note that this variable is only available in CTest.
|
||||
|
||||
``JUNIT_OUTPUT_DIR dir``
|
||||
If specified, the parameter is passed along with ``--reporters=junit``
|
||||
and ``--out=`` to the test executable. The actual file name is the same
|
||||
as the test target, including prefix and suffix. This should be used
|
||||
instead of EXTRA_ARGS to avoid race conditions writing the XML result
|
||||
output when using parallel test execution.
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
function(doctest_discover_tests TARGET)
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;JUNIT_OUTPUT_DIR"
|
||||
"TEST_SPEC;EXTRA_ARGS;PROPERTIES;ADD_LABELS"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if(NOT _WORKING_DIRECTORY)
|
||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
|
||||
## Generate a unique name based on the extra arguments
|
||||
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
|
||||
string(SUBSTRING ${args_hash} 0 7 args_hash)
|
||||
|
||||
# Define rule to generate test list for aforementioned test executable
|
||||
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
|
||||
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
|
||||
get_property(crosscompiling_emulator
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CROSSCOMPILING_EMULATOR
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-D "TEST_TARGET=${TARGET}"
|
||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||
-D "TEST_SPEC=${_TEST_SPEC}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_ADD_LABELS=${_ADD_LABELS}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "TEST_JUNIT_OUTPUT_DIR=${_JUNIT_OUTPUT_DIR}"
|
||||
-D "CTEST_FILE=${ctest_tests_file}"
|
||||
-P "${_DOCTEST_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
file(WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||
" include(\"${ctest_tests_file}\")\n"
|
||||
"else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
|
||||
"endif()\n"
|
||||
)
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.10)
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(DIRECTORY
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||
)
|
||||
else()
|
||||
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
|
||||
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
|
||||
if(NOT ${test_include_file_set})
|
||||
set_property(DIRECTORY
|
||||
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Cannot set more than one TEST_INCLUDE_FILE"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
###############################################################################
|
||||
|
||||
set(_DOCTEST_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_CURRENT_LIST_DIR}/doctestAddTests.cmake
|
||||
)
|
||||
120
3rdparty/simple-protobuf/test/cmake/doctestAddTests.cmake
vendored
Normal file
120
3rdparty/simple-protobuf/test/cmake/doctestAddTests.cmake
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
set(prefix "${TEST_PREFIX}")
|
||||
set(suffix "${TEST_SUFFIX}")
|
||||
set(spec ${TEST_SPEC})
|
||||
set(extra_args ${TEST_EXTRA_ARGS})
|
||||
set(properties ${TEST_PROPERTIES})
|
||||
set(add_labels ${TEST_ADD_LABELS})
|
||||
set(junit_output_dir "${TEST_JUNIT_OUTPUT_DIR}")
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
|
||||
function(add_command NAME)
|
||||
set(_args "")
|
||||
foreach(_arg ${ARGN})
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
set(_args "${_args} ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${spec}" MATCHES .)
|
||||
set(spec "--test-case=${spec}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-cases
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${result}\n"
|
||||
" Output: ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
if("${line}" STREQUAL "===============================================================================" OR "${line}" MATCHES [==[^\[doctest\] ]==])
|
||||
continue()
|
||||
endif()
|
||||
set(test ${line})
|
||||
set(labels "")
|
||||
if(${add_labels})
|
||||
# get test suite that test belongs to
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" --test-case=${test} --list-test-suites
|
||||
OUTPUT_VARIABLE labeloutput
|
||||
RESULT_VARIABLE labelresult
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
)
|
||||
if(NOT ${labelresult} EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable '${TEST_EXECUTABLE}':\n"
|
||||
" Result: ${labelresult}\n"
|
||||
" Output: ${labeloutput}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" labeloutput "${labeloutput}")
|
||||
foreach(labelline ${labeloutput})
|
||||
if("${labelline}" STREQUAL "===============================================================================" OR "${labelline}" MATCHES [==[^\[doctest\] ]==])
|
||||
continue()
|
||||
endif()
|
||||
list(APPEND labels ${labelline})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT "${junit_output_dir}" STREQUAL "")
|
||||
# turn testname into a valid filename by replacing all special characters with "-"
|
||||
string(REGEX REPLACE "[/\\:\"|<>]" "-" test_filename "${test}")
|
||||
set(TEST_JUNIT_OUTPUT_PARAM "--reporters=junit" "--out=${junit_output_dir}/${prefix}${test_filename}${suffix}.xml")
|
||||
else()
|
||||
unset(TEST_JUNIT_OUTPUT_PARAM)
|
||||
endif()
|
||||
# use escape commas to handle properly test cases with commas inside the name
|
||||
string(REPLACE "," "\\," test_name ${test})
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"--test-case=${test_name}"
|
||||
"${TEST_JUNIT_OUTPUT_PARAM}"
|
||||
${extra_args}
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
LABELS ${labels}
|
||||
)
|
||||
unset(labels)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
add_command(set ${TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
file(WRITE "${CTEST_FILE}" "${script}")
|
||||
7134
3rdparty/simple-protobuf/test/doctest.h
vendored
Normal file
7134
3rdparty/simple-protobuf/test/doctest.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
539
3rdparty/simple-protobuf/test/etl-compatibility.cpp
vendored
Normal file
539
3rdparty/simple-protobuf/test/etl-compatibility.cpp
vendored
Normal file
@ -0,0 +1,539 @@
|
||||
#include <cstdint>
|
||||
#include <etl-person.pb.h>
|
||||
#include <etl-scalar.pb.h>
|
||||
#include <google/protobuf/util/json_util.h>
|
||||
#include <gpb-etl-person.pb.h>
|
||||
#include <gpb-etl-scalar.pb.h>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest.h"
|
||||
|
||||
namespace std
|
||||
{
|
||||
template < size_t S >
|
||||
auto operator==( const string & lhs, const ::etl::vector< byte, S > & rhs ) noexcept -> bool
|
||||
{
|
||||
return lhs.size( ) == rhs.size( ) && ( memcmp( lhs.data( ), rhs.data( ), lhs.size( ) ) == 0 );
|
||||
}
|
||||
template < size_t S >
|
||||
auto operator==( const ::etl::string< S > & lhs, const string & rhs ) noexcept -> bool
|
||||
{
|
||||
return lhs.size( ) == rhs.size( ) && ( memcmp( lhs.data( ), rhs.data( ), lhs.size( ) ) == 0 );
|
||||
}
|
||||
}// namespace std
|
||||
|
||||
namespace Test
|
||||
{
|
||||
|
||||
namespace Etl::Scalar
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
concept HasValueMember = requires( T t ) {
|
||||
{
|
||||
t.value
|
||||
};
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
requires HasValueMember< T >
|
||||
auto operator==( const T & lhs, const T & rhs ) noexcept -> bool
|
||||
{
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
}// namespace Etl::Scalar
|
||||
}// namespace Test
|
||||
|
||||
namespace PhoneBook::Etl
|
||||
{
|
||||
auto operator==( const Person::PhoneNumber & lhs, const Person::PhoneNumber & rhs ) noexcept -> bool
|
||||
{
|
||||
return lhs.number == rhs.number && lhs.type == rhs.type;
|
||||
}
|
||||
|
||||
auto operator==( const Person & lhs, const Person & rhs ) noexcept -> bool
|
||||
{
|
||||
return lhs.name == rhs.name && lhs.id == rhs.id && lhs.email == rhs.email && lhs.phones == rhs.phones;
|
||||
}
|
||||
}// namespace PhoneBook::Etl
|
||||
|
||||
namespace
|
||||
{
|
||||
auto to_bytes( std::string_view str ) -> etl::vector< std::byte, 8 >
|
||||
{
|
||||
auto span = std::span< std::byte >( ( std::byte * ) str.data( ), str.size( ) );
|
||||
return { span.data( ), span.data( ) + span.size( ) };
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
concept is_gpb_repeated = requires( T t ) {
|
||||
{
|
||||
t.value( 0 )
|
||||
};
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
auto opt_size( const std::optional< T > & opt ) -> std::size_t
|
||||
{
|
||||
if( opt.has_value( ) )
|
||||
{
|
||||
return opt.value( ).size( );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template < typename T, size_t S >
|
||||
auto opt_size( const etl::vector< T, S > & opt ) -> std::size_t
|
||||
{
|
||||
return opt.size( );
|
||||
}
|
||||
|
||||
template < typename GPB, typename SPB >
|
||||
void gpb_test( const SPB & spb )
|
||||
{
|
||||
auto gpb = GPB( );
|
||||
auto spb_serialized = spb::pb::serialize( spb );
|
||||
|
||||
REQUIRE( gpb.ParseFromString( spb_serialized ) );
|
||||
if constexpr( is_gpb_repeated< GPB > )
|
||||
{
|
||||
REQUIRE( gpb.value( ).size( ) == opt_size( spb.value ) );
|
||||
for( size_t i = 0; i < opt_size( spb.value ); ++i )
|
||||
{
|
||||
REQUIRE( gpb.value( i ) == spb.value[ i ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE( spb.value == gpb.value( ) );
|
||||
}
|
||||
auto gpb_serialized = std::string( );
|
||||
REQUIRE( gpb.SerializeToString( &gpb_serialized ) );
|
||||
REQUIRE( spb::pb::deserialize< SPB >( gpb_serialized ) == spb );
|
||||
}
|
||||
|
||||
template < typename GPB, typename SPB >
|
||||
void gpb_json( const SPB & spb )
|
||||
{
|
||||
auto gpb = GPB( );
|
||||
auto spb_serialized = spb::json::serialize( spb );
|
||||
|
||||
auto parse_options = google::protobuf::util::JsonParseOptions{ };
|
||||
( void ) JsonStringToMessage( spb_serialized, &gpb, parse_options );
|
||||
|
||||
if constexpr( is_gpb_repeated< GPB > )
|
||||
{
|
||||
REQUIRE( gpb.value( ).size( ) == opt_size( spb.value ) );
|
||||
for( size_t i = 0; i < opt_size( spb.value ); ++i )
|
||||
{
|
||||
REQUIRE( gpb.value( i ) == spb.value[ i ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
REQUIRE( spb.value == gpb.value( ) );
|
||||
}
|
||||
auto json_string = std::string( );
|
||||
auto print_options = google::protobuf::util::JsonPrintOptions( );
|
||||
print_options.preserve_proto_field_names = true;
|
||||
|
||||
( void ) MessageToJsonString( gpb, &json_string, print_options );
|
||||
REQUIRE( spb::json::deserialize< SPB >( json_string ) == spb );
|
||||
|
||||
print_options.preserve_proto_field_names = false;
|
||||
json_string.clear( );
|
||||
( void ) MessageToJsonString( gpb, &json_string, print_options );
|
||||
REQUIRE( spb::json::deserialize< SPB >( json_string ) == spb );
|
||||
}
|
||||
|
||||
template < typename GPB, typename SPB >
|
||||
void gpb_compatibility( const SPB & spb )
|
||||
{
|
||||
SUBCASE( "gpb serialize/deserialize" )
|
||||
{
|
||||
gpb_test< GPB, SPB >( spb );
|
||||
}
|
||||
SUBCASE( "json serialize/deserialize" )
|
||||
{
|
||||
gpb_json< GPB, SPB >( spb );
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
TEST_CASE( "string" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqString >( Test::Etl::Scalar::ReqString{ .value = "hello" } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptString >( Test::Etl::Scalar::OptString{ .value = "hello" } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepString >( Test::Etl::Scalar::RepString{ .value = { "hello" } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepString >( Test::Etl::Scalar::RepString{ .value = { "hello", "world" } } );
|
||||
}
|
||||
}
|
||||
TEST_CASE( "bool" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqBool >( Test::Etl::Scalar::ReqBool{ .value = true } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqBool >( Test::Etl::Scalar::ReqBool{ .value = false } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptBool >( Test::Etl::Scalar::OptBool{ .value = true } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptBool >( Test::Etl::Scalar::OptBool{ .value = false } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepBool >( Test::Etl::Scalar::RepBool{ .value = { true } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepBool >( Test::Etl::Scalar::RepBool{ .value = { true, false } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackBool >( Test::Etl::Scalar::RepPackBool{ .value = { true } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackBool >( Test::Etl::Scalar::RepPackBool{ .value = { true, false } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_CASE( "int" )
|
||||
{
|
||||
SUBCASE( "varint32" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqInt32 >( Test::Etl::Scalar::ReqInt32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqInt32 >( Test::Etl::Scalar::ReqInt32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqInt32 >( Test::Etl::Scalar::ReqInt32{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptInt32 >( Test::Etl::Scalar::OptInt32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptInt32 >( Test::Etl::Scalar::OptInt32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptInt32 >( Test::Etl::Scalar::OptInt32{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepInt32 >( Test::Etl::Scalar::RepInt32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepInt32 >( Test::Etl::Scalar::RepInt32{ .value = { 0x42, 0x3 } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackInt32 >( Test::Etl::Scalar::RepPackInt32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackInt32 >( Test::Etl::Scalar::RepPackInt32{ .value = { 0x42, 0x3 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "varint64" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqInt64 >( Test::Etl::Scalar::ReqInt64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqInt64 >( Test::Etl::Scalar::ReqInt64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqInt64 >( Test::Etl::Scalar::ReqInt64{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptInt64 >( Test::Etl::Scalar::OptInt64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptInt64 >( Test::Etl::Scalar::OptInt64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptInt64 >( Test::Etl::Scalar::OptInt64{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepInt64 >( Test::Etl::Scalar::RepInt64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepInt64 >( Test::Etl::Scalar::RepInt64{ .value = { 0x42, 0x3 } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackInt64 >( Test::Etl::Scalar::RepPackInt64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackInt64 >( Test::Etl::Scalar::RepPackInt64{ .value = { 0x42, 0x3 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "svarint32" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSint32 >( Test::Etl::Scalar::ReqSint32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSint32 >( Test::Etl::Scalar::ReqSint32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSint32 >( Test::Etl::Scalar::ReqSint32{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSint32 >( Test::Etl::Scalar::OptSint32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSint32 >( Test::Etl::Scalar::OptSint32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSint32 >( Test::Etl::Scalar::OptSint32{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSint32 >( Test::Etl::Scalar::RepSint32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSint32 >( Test::Etl::Scalar::RepSint32{ .value = { 0x42, -2 } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSint32 >( Test::Etl::Scalar::RepPackSint32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSint32 >( Test::Etl::Scalar::RepPackSint32{ .value = { 0x42, -2 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "svarint64" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSint64 >( Test::Etl::Scalar::ReqSint64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSint64 >( Test::Etl::Scalar::ReqSint64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSint64 >( Test::Etl::Scalar::ReqSint64{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSint64 >( Test::Etl::Scalar::OptSint64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSint64 >( Test::Etl::Scalar::OptSint64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSint64 >( Test::Etl::Scalar::OptSint64{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSint64 >( Test::Etl::Scalar::RepSint64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSint64 >( Test::Etl::Scalar::RepSint64{ .value = { 0x42, -2 } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSint64 >( Test::Etl::Scalar::RepPackSint64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSint64 >( Test::Etl::Scalar::RepPackSint64{ .value = { 0x42, -2 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "fixed32" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqFixed32 >( Test::Etl::Scalar::ReqFixed32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqFixed32 >( Test::Etl::Scalar::ReqFixed32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqFixed32 >( Test::Etl::Scalar::ReqFixed32{ .value = uint32_t( -2 ) } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptFixed32 >( Test::Etl::Scalar::OptFixed32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptFixed32 >( Test::Etl::Scalar::OptFixed32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptFixed32 >( Test::Etl::Scalar::OptFixed32{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepFixed32 >( Test::Etl::Scalar::RepFixed32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepFixed32 >( Test::Etl::Scalar::RepFixed32{ .value = { 0x42, 0x3 } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackFixed32 >( Test::Etl::Scalar::RepPackFixed32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackFixed32 >( Test::Etl::Scalar::RepPackFixed32{ .value = { 0x42, 0x3 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "fixed64" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqFixed64 >( Test::Etl::Scalar::ReqFixed64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqFixed64 >( Test::Etl::Scalar::ReqFixed64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqFixed64 >( Test::Etl::Scalar::ReqFixed64{ .value = uint64_t( -2 ) } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptFixed64 >( Test::Etl::Scalar::OptFixed64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptFixed64 >( Test::Etl::Scalar::OptFixed64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptFixed64 >( Test::Etl::Scalar::OptFixed64{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepFixed64 >( Test::Etl::Scalar::RepFixed64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepFixed64 >( Test::Etl::Scalar::RepFixed64{ .value = { 0x42, 0x3 } } );
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackFixed64 >( Test::Etl::Scalar::RepPackFixed64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackFixed64 >( Test::Etl::Scalar::RepPackFixed64{ .value = { 0x42, 0x3 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "sfixed32" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSfixed32 >( Test::Etl::Scalar::ReqSfixed32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSfixed32 >( Test::Etl::Scalar::ReqSfixed32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSfixed32 >( Test::Etl::Scalar::ReqSfixed32{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSfixed32 >( Test::Etl::Scalar::OptSfixed32{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSfixed32 >( Test::Etl::Scalar::OptSfixed32{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSfixed32 >( Test::Etl::Scalar::OptSfixed32{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSfixed32 >( Test::Etl::Scalar::RepSfixed32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSfixed32 >( Test::Etl::Scalar::RepSfixed32{ .value = { 0x42, 0x3 } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSfixed32 >( Test::Etl::Scalar::RepPackSfixed32{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSfixed32 >( Test::Etl::Scalar::RepPackSfixed32{ .value = { 0x42, 0x3 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "sfixed64" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSfixed64 >( Test::Etl::Scalar::ReqSfixed64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSfixed64 >( Test::Etl::Scalar::ReqSfixed64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqSfixed64 >( Test::Etl::Scalar::ReqSfixed64{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSfixed64 >( Test::Etl::Scalar::OptSfixed64{ .value = 0x42 } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSfixed64 >( Test::Etl::Scalar::OptSfixed64{ .value = 0xff } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptSfixed64 >( Test::Etl::Scalar::OptSfixed64{ .value = -2 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSfixed64 >( Test::Etl::Scalar::RepSfixed64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepSfixed64 >( Test::Etl::Scalar::RepSfixed64{ .value = { 0x42, 0x3 } } );
|
||||
|
||||
SUBCASE( "packed" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSfixed64 >( Test::Etl::Scalar::RepPackSfixed64{ .value = { 0x42 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepPackSfixed64 >( Test::Etl::Scalar::RepPackSfixed64{ .value = { 0x42, 0x3 } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_CASE( "float" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqFloat >( Test::Etl::Scalar::ReqFloat{ .value = 42.0 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptFloat >( Test::Etl::Scalar::OptFloat{ .value = 42.3 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepFloat >( Test::Etl::Scalar::RepFloat{ .value = { 42.3 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepFloat >( Test::Etl::Scalar::RepFloat{ .value = { 42.0, 42.3 } } );
|
||||
}
|
||||
}
|
||||
TEST_CASE( "double" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqDouble >( Test::Etl::Scalar::ReqDouble{ .value = 42.0 } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptDouble >( Test::Etl::Scalar::OptDouble{ .value = 42.3 } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepDouble >( Test::Etl::Scalar::RepDouble{ .value = { 42.3 } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepDouble >( Test::Etl::Scalar::RepDouble{ .value = { 42.3, 3.0 } } );
|
||||
}
|
||||
}
|
||||
TEST_CASE( "bytes" )
|
||||
{
|
||||
SUBCASE( "required" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqBytes >( Test::Etl::Scalar::ReqBytes{ .value = to_bytes( "hello" ) } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqBytes >( Test::Etl::Scalar::ReqBytes{ .value = to_bytes( "\x00\x01\x02"sv ) } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::ReqBytes >( Test::Etl::Scalar::ReqBytes{ .value = to_bytes( "\x00\x01\x02\x03\x04"sv ) } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptBytes >( Test::Etl::Scalar::OptBytes{ .value = to_bytes( "hello" ) } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptBytes >( Test::Etl::Scalar::OptBytes{ .value = to_bytes( "\x00\x01\x02"sv ) } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::OptBytes >( Test::Etl::Scalar::OptBytes{ .value = to_bytes( "\x00\x01\x02\x03\x04"sv ) } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepBytes >( Test::Etl::Scalar::RepBytes{ .value = { to_bytes( "hello" ) } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepBytes >( Test::Etl::Scalar::RepBytes{ .value = { to_bytes( "\x00\x01\x02"sv ) } } );
|
||||
gpb_compatibility< Test::Etl::Scalar::gpb::RepBytes >( Test::Etl::Scalar::RepBytes{ .value = { to_bytes( "\x00\x01\x02\x03\x04"sv ) } } );
|
||||
}
|
||||
}
|
||||
TEST_CASE( "person" )
|
||||
{
|
||||
auto gpb = PhoneBook::Etl::gpb::Person( );
|
||||
auto spb = PhoneBook::Etl::Person{
|
||||
.name = "John Doe",
|
||||
.id = 123,
|
||||
.email = "QXUeh@example.com",
|
||||
.phones = {
|
||||
PhoneBook::Etl::Person::PhoneNumber{
|
||||
.number = "555-4321",
|
||||
.type = PhoneBook::Etl::Person::PhoneType::HOME,
|
||||
},
|
||||
PhoneBook::Etl::Person::PhoneNumber{
|
||||
.number = "999-1234",
|
||||
.type = PhoneBook::Etl::Person::PhoneType::MOBILE,
|
||||
},
|
||||
}
|
||||
};
|
||||
SUBCASE( "gpb serialize/deserialize" )
|
||||
{
|
||||
auto spb_serialized = spb::pb::serialize( spb );
|
||||
|
||||
REQUIRE( gpb.ParseFromString( spb_serialized ) );
|
||||
REQUIRE( gpb.name( ) == spb.name );
|
||||
REQUIRE( gpb.id( ) == spb.id );
|
||||
REQUIRE( gpb.email( ) == spb.email );
|
||||
REQUIRE( gpb.phones_size( ) == 2 );
|
||||
for( auto i = 0; i < gpb.phones_size( ); i++ )
|
||||
{
|
||||
REQUIRE( gpb.phones( i ).number( ) == spb.phones[ i ].number );
|
||||
REQUIRE( int( gpb.phones( i ).type( ) ) == int( spb.phones[ i ].type.value( ) ) );
|
||||
}
|
||||
|
||||
auto gpb_serialized = std::string( );
|
||||
REQUIRE( gpb.SerializeToString( &gpb_serialized ) );
|
||||
REQUIRE( spb::pb::deserialize< PhoneBook::Etl::Person >( gpb_serialized ) == spb );
|
||||
}
|
||||
SUBCASE( "json serialize/deserialize" )
|
||||
{
|
||||
auto spb_json = spb::json::serialize( spb );
|
||||
|
||||
auto parse_options = google::protobuf::util::JsonParseOptions{ };
|
||||
( void ) JsonStringToMessage( spb_json, &gpb, parse_options );
|
||||
|
||||
REQUIRE( gpb.name( ) == spb.name );
|
||||
REQUIRE( gpb.id( ) == spb.id );
|
||||
REQUIRE( gpb.email( ) == spb.email );
|
||||
REQUIRE( gpb.phones_size( ) == 2 );
|
||||
for( auto i = 0; i < gpb.phones_size( ); i++ )
|
||||
{
|
||||
REQUIRE( gpb.phones( i ).number( ) == spb.phones[ i ].number );
|
||||
REQUIRE( int( gpb.phones( i ).type( ) ) == int( spb.phones[ i ].type.value( ) ) );
|
||||
}
|
||||
|
||||
auto json_string = std::string( );
|
||||
auto print_options = google::protobuf::util::JsonPrintOptions( );
|
||||
print_options.preserve_proto_field_names = true;
|
||||
|
||||
( void ) MessageToJsonString( gpb, &json_string, print_options );
|
||||
REQUIRE( spb::json::deserialize< PhoneBook::Etl::Person >( json_string ) == spb );
|
||||
|
||||
print_options.preserve_proto_field_names = false;
|
||||
json_string.clear( );
|
||||
( void ) MessageToJsonString( gpb, &json_string, print_options );
|
||||
REQUIRE( spb::json::deserialize< PhoneBook::Etl::Person >( json_string ) == spb );
|
||||
}
|
||||
}
|
||||
28
3rdparty/simple-protobuf/test/fuzzer/spb-json-fuzzer.cpp
vendored
Normal file
28
3rdparty/simple-protobuf/test/fuzzer/spb-json-fuzzer.cpp
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <map.pb.h>
|
||||
#include <person.pb.h>
|
||||
#include <proto3.pb.h>
|
||||
#include <scalar.pb.h>
|
||||
#include <string_view>
|
||||
|
||||
template < typename T >
|
||||
static void json_decode( const uint8_t * Data, size_t Size )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto result = spb::json::deserialize< T >( { reinterpret_cast< const char * >( Data ), Size } );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput( const uint8_t * Data, size_t Size )
|
||||
{
|
||||
json_decode< PhoneBook::Person >( Data, Size );
|
||||
json_decode< proto3_unittest::TestAllTypes >( Data, Size );
|
||||
json_decode< proto3_unittest::TestEmptyMessage >( Data, Size );
|
||||
|
||||
return 0;
|
||||
}
|
||||
28
3rdparty/simple-protobuf/test/fuzzer/spb-pb-fuzzer.cpp
vendored
Normal file
28
3rdparty/simple-protobuf/test/fuzzer/spb-pb-fuzzer.cpp
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <map.pb.h>
|
||||
#include <person.pb.h>
|
||||
#include <proto3.pb.h>
|
||||
#include <scalar.pb.h>
|
||||
#include <string_view>
|
||||
|
||||
template < typename T >
|
||||
static void pb_decode( const uint8_t * Data, size_t Size )
|
||||
{
|
||||
try
|
||||
{
|
||||
auto result = spb::pb::deserialize< T >( { reinterpret_cast< const char * >( Data ), Size } );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput( const uint8_t * Data, size_t Size )
|
||||
{
|
||||
pb_decode< PhoneBook::Person >( Data, Size );
|
||||
pb_decode< proto3_unittest::TestAllTypes >( Data, Size );
|
||||
pb_decode< proto3_unittest::TestEmptyMessage >( Data, Size );
|
||||
|
||||
return 0;
|
||||
}
|
||||
20
3rdparty/simple-protobuf/test/fuzzer/spb-protoc-fuzzer.cpp
vendored
Normal file
20
3rdparty/simple-protobuf/test/fuzzer/spb-protoc-fuzzer.cpp
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
#include <parser/parser.h>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput( const uint8_t * Data, size_t Size )
|
||||
{
|
||||
auto file = proto_file{
|
||||
.content = std::string( reinterpret_cast< const char * >( Data ), Size ),
|
||||
};
|
||||
try
|
||||
{
|
||||
parse_proto_file_content( file );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
1422
3rdparty/simple-protobuf/test/gpb-compatibility.cpp
vendored
Normal file
1422
3rdparty/simple-protobuf/test/gpb-compatibility.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
923
3rdparty/simple-protobuf/test/json.cpp
vendored
Normal file
923
3rdparty/simple-protobuf/test/json.cpp
vendored
Normal file
@ -0,0 +1,923 @@
|
||||
#include "spb/json.hpp"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <name.pb.h>
|
||||
#include <person.pb.h>
|
||||
#include <scalar.pb.h>
|
||||
#include <spb/json/deserialize.hpp>
|
||||
#include <spb/json/serialize.hpp>
|
||||
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest.h"
|
||||
|
||||
namespace std
|
||||
{
|
||||
auto operator==( const std::span< const std::byte > & lhs,
|
||||
const std::span< const std::byte > & rhs ) noexcept -> bool
|
||||
{
|
||||
return lhs.size( ) == rhs.size( ) && memcmp( lhs.data( ), rhs.data( ), lhs.size( ) ) == 0;
|
||||
}
|
||||
}// namespace std
|
||||
|
||||
namespace
|
||||
{
|
||||
template < typename T >
|
||||
concept HasValueMember = requires( T t ) {
|
||||
{ t.value };
|
||||
};
|
||||
|
||||
template < size_t N >
|
||||
auto to_array( const char ( &string )[ N ] )
|
||||
{
|
||||
auto result = std::array< std::byte, N - 1 >( );
|
||||
memcpy( result.data( ), &string, result.size( ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
auto to_bytes( std::string_view str ) -> std::vector< std::byte >
|
||||
{
|
||||
auto span = std::span< std::byte >( ( std::byte * ) str.data( ), str.size( ) );
|
||||
return { span.data( ), span.data( ) + span.size( ) };
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
void json_test( const T & value, std::string_view json )
|
||||
{
|
||||
{
|
||||
auto serialized = spb::json::serialize( value );
|
||||
CHECK( serialized == json );
|
||||
auto size = spb::json::serialize_size( value );
|
||||
CHECK( size == json.size( ) );
|
||||
}
|
||||
|
||||
{
|
||||
auto deserialized = spb::json::deserialize< T >( json );
|
||||
if constexpr( HasValueMember< T > )
|
||||
{
|
||||
using valueT = decltype( T::value );
|
||||
CHECK( valueT( deserialized.value ) == valueT( value.value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK( deserialized == value );
|
||||
}
|
||||
}
|
||||
{
|
||||
auto deserialized = T( );
|
||||
spb::json::deserialize( deserialized, json );
|
||||
if constexpr( HasValueMember< T > )
|
||||
{
|
||||
using valueT = decltype( T::value );
|
||||
CHECK( valueT( deserialized.value ) == valueT( value.value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK( deserialized == value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
namespace Test
|
||||
{
|
||||
auto operator==( const Test::Name & lhs, const Test::Name & rhs ) noexcept -> bool
|
||||
{
|
||||
return lhs.name == rhs.name && lhs.bkfvdzz == rhs.bkfvdzz;
|
||||
}
|
||||
}// namespace Test
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
TEST_CASE( "json" )
|
||||
{
|
||||
SUBCASE( "dj2hash" )
|
||||
{
|
||||
const auto hash = spb::json::detail::djb2_hash( "hello" );
|
||||
const auto collision = spb::json::detail::djb2_hash( "narpjy" );
|
||||
const auto hash2 = spb::json::detail::djb2_hash( "world" );
|
||||
const auto name_hash = spb::json::detail::djb2_hash( "name" );
|
||||
const auto name_collision = spb::json::detail::djb2_hash( "bkfvdzz" );
|
||||
const auto hash3 = spb::json::detail::djb2_hash( { } );
|
||||
CHECK( hash == collision );
|
||||
CHECK( name_hash == name_collision );
|
||||
CHECK( hash3 != 0 );
|
||||
CHECK( hash != 0 );
|
||||
CHECK( hash2 != 0 );
|
||||
CHECK( hash != hash2 );
|
||||
CHECK( hash3 != hash2 );
|
||||
CHECK( hash3 != hash );
|
||||
}
|
||||
SUBCASE( "deserialize" )
|
||||
{
|
||||
SUBCASE( "ignore" )
|
||||
{
|
||||
SUBCASE( "empty" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({})"sv ) == Test::Name{ } );
|
||||
CHECK(
|
||||
spb::json::deserialize< Test::Name >(
|
||||
R"({ })"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >( ""sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >( R"(})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >( R"({)"sv ) );
|
||||
}
|
||||
SUBCASE( "string" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"string":"string"})"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"bkfvdzz":"string"})"sv ) ==
|
||||
Test::Name{ .bkfvdzz = "string" } );
|
||||
CHECK(
|
||||
spb::json::deserialize< Test::Name >(
|
||||
R"({"string ":"string "})"sv ) ==
|
||||
Test::Name{ } );
|
||||
}
|
||||
SUBCASE( "int" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"integer":42})"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"bkfvdzz1":42})"sv ) ==
|
||||
Test::Name{ } );
|
||||
}
|
||||
SUBCASE( "float" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"fl":42.0, "fl2":0})"sv ) ==
|
||||
Test::Name{ } );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"repeated":[42,"hello"]})"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"repeated":[]})"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"name1":[]})"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< Test::Name >( R"({"repeated":[})"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< Test::Name >( R"({"repeated":[)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< Test::Name >( R"({"repeated":[42)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< Test::Name >( R"({"repeated":[42,)"sv ) );
|
||||
}
|
||||
SUBCASE( "bool" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >(
|
||||
R"({"value":true, "value2":false})"sv ) == Test::Name{ } );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >(
|
||||
R"({"value":tru, "value2":false})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >(
|
||||
R"({"value":true, "value2":fals})"sv ) );
|
||||
}
|
||||
SUBCASE( "null" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"value":null})"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< Test::Name >( R"({"value":nul})"sv ) );
|
||||
}
|
||||
SUBCASE( "object" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Name >( R"({"value":{}})"sv ) ==
|
||||
Test::Name{ } );
|
||||
CHECK( spb::json::deserialize< Test::Name >(
|
||||
R"({"value":{"key":"value", "key2":[42]}})"sv ) == Test::Name{ } );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >( R"({"value"})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >(
|
||||
R"({"value":{"key":"value", "key2":[42]})"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< Test::Name >( R"({"value":{"key":}})"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< Test::Name >( R"({"value":{"key"}})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >(
|
||||
R"({"value":{"key":"value")"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Name >(
|
||||
R"({"value":{"key":"value",)"sv ) );
|
||||
}
|
||||
}
|
||||
SUBCASE( "person" )
|
||||
{
|
||||
constexpr std::string_view jsons[] = {
|
||||
R"({"name": "John Doe","id": 123,"email": "QXUeh@example.com", "phones": [ {"number": "555-4321","type": "HOME"}]})",
|
||||
R"({"name": "John Doe","id": 123,"email": "QXUeh@example.com", "phones": [ {"number": "555-4321","type": 1}]})",
|
||||
R"({"name2": "Jack","name": "John Doe","id": 123,"email": "QXUeh@example.com", "phones": [ {"number": "555-4321","type": "HOME"}]})",
|
||||
};
|
||||
for( auto json : jsons )
|
||||
{
|
||||
const auto person = spb::json::deserialize< PhoneBook::Person >( json );
|
||||
CHECK( person.name == "John Doe" );
|
||||
CHECK( person.id == 123 );
|
||||
CHECK( person.email == "QXUeh@example.com" );
|
||||
CHECK( person.phones.size( ) == 1 );
|
||||
CHECK( person.phones[ 0 ].number == "555-4321" );
|
||||
CHECK( person.phones[ 0 ].type == PhoneBook::Person::PhoneType::HOME );
|
||||
}
|
||||
}
|
||||
SUBCASE( "enum" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< PhoneBook::Person::PhoneType >( "\"HOME\""sv ) ==
|
||||
PhoneBook::Person::PhoneType::HOME );
|
||||
CHECK( Test::Scalar::ReqEnumAlias::Enum::EAA_STARTED ==
|
||||
Test::Scalar::ReqEnumAlias::Enum::EAA_RUNNING );
|
||||
CHECK( spb::json::deserialize< Test::Scalar::ReqEnumAlias::Enum >(
|
||||
"\"EAA_STARTED\""sv ) == Test::Scalar::ReqEnumAlias::Enum::EAA_STARTED );
|
||||
CHECK( spb::json::deserialize< Test::Scalar::ReqEnumAlias::Enum >(
|
||||
"\"EAA_STARTED\""sv ) == Test::Scalar::ReqEnumAlias::Enum::EAA_RUNNING );
|
||||
CHECK( spb::json::deserialize< Test::Scalar::ReqEnumAlias::Enum >(
|
||||
"\"EAA_RUNNING\""sv ) == Test::Scalar::ReqEnumAlias::Enum::EAA_STARTED );
|
||||
CHECK( spb::json::deserialize< Test::Scalar::ReqEnumAlias::Enum >(
|
||||
"\"EAA_RUNNING\""sv ) == Test::Scalar::ReqEnumAlias::Enum::EAA_RUNNING );
|
||||
REQUIRE_THROWS(
|
||||
( void ) spb::json::deserialize< PhoneBook::Person::PhoneType >( "3"sv ) );
|
||||
REQUIRE_THROWS(
|
||||
( void ) spb::json::deserialize< PhoneBook::Person::PhoneType >( "HME"sv ) );
|
||||
}
|
||||
SUBCASE( "bool" )
|
||||
{
|
||||
{
|
||||
CHECK( spb::json::deserialize< bool >( "true"sv ) == true );
|
||||
CHECK( spb::json::deserialize< bool >( "false"sv ) == false );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< bool >( "hello"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< bool >( "true1"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< bool >( "true_"sv ) );
|
||||
auto value = false;
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "true"sv ) );
|
||||
CHECK( value );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "false"sv ) );
|
||||
CHECK( value == false );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::vector< bool > >( R"([true,false])"sv ) ==
|
||||
std::vector< bool >{ true, false } );
|
||||
CHECK( spb::json::deserialize< std::vector< bool > >( R"([])"sv ) ==
|
||||
std::vector< bool >{ } );
|
||||
CHECK( spb::json::deserialize< std::vector< bool > >( R"(null)"sv ) ==
|
||||
std::vector< bool >{ } );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< bool > >( R"()"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< bool > >( R"(true)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< bool > >( R"([null])"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< bool > >( R"("hello")"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< bool > >( R"([)"sv ) );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::optional< bool > >( "true"sv ) ==
|
||||
std::optional< bool >( true ) );
|
||||
CHECK( spb::json::deserialize< std::optional< bool > >( "false"sv ) ==
|
||||
std::optional< bool >( false ) );
|
||||
CHECK( spb::json::deserialize< std::optional< bool > >( "null"sv ) ==
|
||||
std::optional< bool >( ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< bool > >( "hello"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::optional< bool > >( R"()"sv ) );
|
||||
|
||||
auto value = std::optional< bool >( );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "true"sv ) );
|
||||
CHECK( value == true );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "false"sv ) );
|
||||
CHECK( value == false );
|
||||
}
|
||||
SUBCASE( "ptr" )
|
||||
{
|
||||
CHECK( *spb::json::deserialize< std::unique_ptr< bool > >( "true"sv ) == true );
|
||||
CHECK( *spb::json::deserialize< std::unique_ptr< bool > >( "false"sv ) == false );
|
||||
CHECK( spb::json::deserialize< std::unique_ptr< bool > >( "null"sv ) ==
|
||||
std::unique_ptr< bool >( ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::unique_ptr< bool > >( "hello"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::unique_ptr< bool > >( R"()"sv ) );
|
||||
|
||||
auto value = std::unique_ptr< bool >( );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "true"sv ) );
|
||||
CHECK( *value == true );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "false"sv ) );
|
||||
CHECK( *value == false );
|
||||
}
|
||||
}
|
||||
SUBCASE( "float" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< float >( "0"sv ) == 0 );
|
||||
CHECK( spb::json::deserialize< float >( "42"sv ) == 42 );
|
||||
CHECK( spb::json::deserialize< float >( "3.14"sv ) == 3.14f );
|
||||
CHECK( spb::json::deserialize< float >( "0.0"sv ) == 0.0f );
|
||||
CHECK( spb::json::deserialize< float >( "-0.0"sv ) == -0.0f );
|
||||
CHECK( spb::json::deserialize< float >( "-3.14"sv ) == -3.14f );
|
||||
CHECK( spb::json::deserialize< float >( "3.14e+10"sv ) == 3.14e+10f );
|
||||
CHECK( spb::json::deserialize< float >( "3.14e-10"sv ) == 3.14e-10f );
|
||||
CHECK( spb::json::deserialize< float >( "3.14E+10"sv ) == 3.14E+10f );
|
||||
CHECK( spb::json::deserialize< float >( "3.14E-10"sv ) == 3.14E-10f );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< float >( "hello"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< float >( ""sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< float >( "true"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< float >( R"("hello")"sv ) );
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::vector< float > >( "[]"sv ) ==
|
||||
std::vector< float >{ } );
|
||||
CHECK( spb::json::deserialize< std::vector< float > >( "null"sv ) ==
|
||||
std::vector< float >{ } );
|
||||
CHECK( spb::json::deserialize< std::vector< float > >( "[42]"sv ) ==
|
||||
std::vector< float >{ 42 } );
|
||||
CHECK( spb::json::deserialize< std::vector< float > >( "[42,3.14]"sv ) ==
|
||||
std::vector< float >{ 42, 3.14f } );
|
||||
CHECK( spb::json::deserialize< std::vector< float > >( "[42,3.14,0.0]"sv ) ==
|
||||
std::vector< float >{ 42, 3.14f, 0.0f } );
|
||||
CHECK( spb::json::deserialize< std::vector< float > >( "[42,3.14,-0.0]"sv ) ==
|
||||
std::vector< float >{ 42, 3.14f, -0.0f } );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< float > >( "42"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< float > >( "true"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< float > >( "hello"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< float > >( R"([)"sv ) );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
auto value = std::optional< float >( );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "42"sv ) );
|
||||
CHECK( *value == 42.0f );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "3.14"sv ) );
|
||||
CHECK( *value == 3.14f );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "0.0"sv ) );
|
||||
CHECK( *value == 0.0f );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "-3.14"sv ) );
|
||||
CHECK( *value == -3.14f );
|
||||
CHECK( spb::json::deserialize< std::optional< float > >( "42"sv ) == 42 );
|
||||
CHECK( spb::json::deserialize< std::optional< float > >( "null"sv ) ==
|
||||
std::nullopt );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< float > >( "hello"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::optional< float > >( ""sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< float > >( "true"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< float > >( R"("hello")"sv ) );
|
||||
}
|
||||
}
|
||||
SUBCASE( "int32" )
|
||||
{
|
||||
|
||||
CHECK( spb::json::deserialize< int32_t >( "42"sv ) == 42 );
|
||||
CHECK( spb::json::deserialize< int32_t >( "\"42\""sv ) == 42 );
|
||||
CHECK( spb::json::deserialize< int32_t >( "-42"sv ) == -42 );
|
||||
CHECK( spb::json::deserialize< int32_t >( "0"sv ) == 0 );
|
||||
CHECK( spb::json::deserialize< int32_t >( "\"-0\""sv ) == 0 );
|
||||
CHECK( spb::json::deserialize< int32_t >( "2147483647"sv ) == 2147483647 );
|
||||
CHECK( spb::json::deserialize< int32_t >( "-2147483648"sv ) == -2147483648 );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< int32_t >( "hello"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< int32_t >( ""sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< int32_t >( "true"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< int32_t >( R"("hello")"sv ) );
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::vector< int32_t > >( R"([42,-42,0])"sv ) ==
|
||||
std::vector< int32_t >{ 42, -42, 0 } );
|
||||
CHECK( spb::json::deserialize< std::vector< int32_t > >(
|
||||
R"([-2147483648,2147483647])"sv ) ==
|
||||
std::vector< int32_t >{ -2147483648, 2147483647 } );
|
||||
CHECK( spb::json::deserialize< std::vector< int32_t > >( R"([])"sv ) ==
|
||||
std::vector< int32_t >( ) );
|
||||
CHECK( spb::json::deserialize< std::vector< int32_t > >( R"(null)"sv ) ==
|
||||
std::vector< int32_t >( ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< int32_t > >( R"([)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< int32_t > >( R"(])"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< int32_t > >( R"([42,)"sv ) );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::optional< int32_t > >( "42"sv ) ==
|
||||
std::optional< int32_t >( 42 ) );
|
||||
CHECK( spb::json::deserialize< std::optional< int32_t > >( "-42"sv ) ==
|
||||
std::optional< int32_t >( -42 ) );
|
||||
CHECK( spb::json::deserialize< std::optional< int32_t > >( "0"sv ) ==
|
||||
std::optional< int32_t >( 0 ) );
|
||||
CHECK( spb::json::deserialize< std::optional< int32_t > >( "2147483647"sv ) ==
|
||||
std::optional< int32_t >( 2147483647 ) );
|
||||
CHECK( spb::json::deserialize< std::optional< int32_t > >( "-2147483648"sv ) ==
|
||||
std::optional< int32_t >( -2147483648 ) );
|
||||
CHECK( spb::json::deserialize< std::optional< int32_t > >( "null"sv ) ==
|
||||
std::optional< int32_t >( ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< int32_t > >( "hello"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< int32_t > >( R"()"sv ) );
|
||||
}
|
||||
SUBCASE( "ptr" )
|
||||
{
|
||||
auto value = std::unique_ptr< int32_t >( );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "42"sv ) );
|
||||
CHECK( *value == 42 );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "-42"sv ) );
|
||||
CHECK( *value == -42 );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "0"sv ) );
|
||||
CHECK( *value == 0 );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "2147483647"sv ) );
|
||||
CHECK( *value == 2147483647 );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "-2147483648"sv ) );
|
||||
CHECK( *value == -2147483648 );
|
||||
CHECK_NOTHROW( spb::json::deserialize( value, "null"sv ) );
|
||||
CHECK( value == nullptr );
|
||||
}
|
||||
SUBCASE( "bitfield" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< Test::Scalar::ReqUint8_1 >( R"({"value":0})"sv )
|
||||
.value == 0 );
|
||||
CHECK( spb::json::deserialize< Test::Scalar::ReqUint8_1 >( R"({"value":1})"sv )
|
||||
.value == 1 );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< Test::Scalar::ReqUint8_1 >(
|
||||
R"({"value":2})"sv ) );
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK(
|
||||
spb::json::deserialize< std::vector< std::string > >( R"(["hello","world"])"sv ) ==
|
||||
std::vector< std::string >{ "hello", "world" } );
|
||||
CHECK( spb::json::deserialize< std::vector< std::string > >( R"([])"sv ) ==
|
||||
std::vector< std::string >{ } );
|
||||
CHECK( spb::json::deserialize< std::vector< std::string > >( R"(null)"sv ) ==
|
||||
std::vector< std::string >{ } );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::string > >( R"()"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::string > >( R"(true)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::string > >( R"("hello"])"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::string > >( R"([)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::string > >( R"(["hello")"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::string > >( R"(["hello",)"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< std::string > >(
|
||||
R"(["hello",])"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< std::string > >(
|
||||
R"(["hello","world")"sv ) );
|
||||
}
|
||||
SUBCASE( "bytes" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::vector< std::byte > >( R"("AAECAwQ=")"sv ) ==
|
||||
to_bytes( "\x00\x01\x02\x03\x04"sv ) );
|
||||
CHECK( spb::json::deserialize< std::vector< std::byte > >( R"("aGVsbG8=")"sv ) ==
|
||||
to_bytes( "hello" ) );
|
||||
CHECK( spb::json::deserialize< std::vector< std::byte > >( R"(null)"sv ) ==
|
||||
std::vector< std::byte >{ } );
|
||||
CHECK( spb::json::deserialize< std::vector< std::byte > >( R"("")"sv ) ==
|
||||
std::vector< std::byte >{ } );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::byte > >( R"(true)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::byte > >( R"("AAECAwQ")"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::vector< std::byte > >( R"([])"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::vector< std::byte > >( R"()"sv ) );
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::vector< std::vector< std::byte > > >(
|
||||
R"(["AAECAwQ="])"sv ) ==
|
||||
std::vector< std::vector< std::byte > >{
|
||||
to_bytes( "\x00\x01\x02\x03\x04"sv ) } );
|
||||
CHECK( spb::json::deserialize< std::vector< std::vector< std::byte > > >(
|
||||
R"(["AAECAwQ=","aGVsbG8="])"sv ) ==
|
||||
std::vector< std::vector< std::byte > >{
|
||||
to_bytes( "\x00\x01\x02\x03\x04"sv ), to_bytes( "hello" ) } );
|
||||
CHECK( spb::json::deserialize< std::vector< std::vector< std::byte > > >(
|
||||
R"([])"sv ) == std::vector< std::vector< std::byte > >{ } );
|
||||
CHECK( spb::json::deserialize< std::vector< std::vector< std::byte > > >(
|
||||
R"(null)"sv ) == std::vector< std::vector< std::byte > >{ } );
|
||||
CHECK( spb::json::deserialize< std::vector< std::vector< std::byte > > >(
|
||||
R"([""])"sv ) ==
|
||||
std::vector< std::vector< std::byte > >{ std::vector< std::byte >{} } );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"(null)"sv ) == std::nullopt );
|
||||
CHECK( spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"("AAECAwQ=")"sv ) ==
|
||||
std::vector< std::byte >{ std::byte( 0 ), std::byte( 1 ), std::byte( 2 ),
|
||||
std::byte( 3 ), std::byte( 4 ) } );
|
||||
CHECK( spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"("aGVsbG8=")"sv ) ==
|
||||
std::vector< std::byte >{ std::byte( 'h' ), std::byte( 'e' ),
|
||||
std::byte( 'l' ), std::byte( 'l' ),
|
||||
std::byte( 'o' ) } );
|
||||
CHECK( spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"("")"sv ) == std::vector< std::byte >{ } );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"(true)"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"("AAECAwQ")"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"([])"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::optional< std::vector< std::byte > > >(
|
||||
R"()"sv ) );
|
||||
}
|
||||
}
|
||||
SUBCASE( "map" )
|
||||
{
|
||||
SUBCASE( "int32/int32" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::map< int32_t, int32_t > >( R"({"1":2})"sv ) ==
|
||||
std::map< int32_t, int32_t >{ { 1, 2 } } );
|
||||
CHECK( spb::json::deserialize< std::map< int32_t, int32_t > >(
|
||||
R"({"1":2,"2":3})"sv ) ==
|
||||
std::map< int32_t, int32_t >{ { 1, 2 }, { 2, 3 } } );
|
||||
CHECK( spb::json::deserialize< std::map< int32_t, int32_t > >( R"({})"sv ) ==
|
||||
std::map< int32_t, int32_t >{ } );
|
||||
CHECK( spb::json::deserialize< std::map< int32_t, int32_t > >( R"(null)"sv ) ==
|
||||
std::map< int32_t, int32_t >{ } );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::map< int32_t, int32_t > >( R"()"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< int32_t, int32_t > >(
|
||||
R"("hello")"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< int32_t, int32_t > >(
|
||||
R"({"hello":2})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< int32_t, int32_t > >(
|
||||
R"({"1":"hello"})"sv ) );
|
||||
}
|
||||
SUBCASE( "string/string" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::map< std::string, std::string > >(
|
||||
R"({"hello":"world"})"sv ) ==
|
||||
std::map< std::string, std::string >{ { "hello", "world" } } );
|
||||
CHECK( spb::json::deserialize< std::map< std::string, std::string > >(
|
||||
R"({})"sv ) == std::map< std::string, std::string >{ } );
|
||||
CHECK( spb::json::deserialize< std::map< std::string, std::string > >(
|
||||
R"(null)"sv ) == std::map< std::string, std::string >{ } );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::map< std::string, std::string > >(
|
||||
R"()"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::map< std::string, std::string > >(
|
||||
R"({"1":["hello"]})"sv ) );
|
||||
CHECK_THROWS(
|
||||
( void ) spb::json::deserialize< std::map< std::string, std::string > >(
|
||||
R"({"hello":{"hello":"world"}]})"sv ) );
|
||||
}
|
||||
SUBCASE( "int32/string" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::map< int32_t, std::string > >(
|
||||
R"({"1":"hello"})"sv ) ==
|
||||
std::map< int32_t, std::string >{ { 1, "hello" } } );
|
||||
CHECK( spb::json::deserialize< std::map< int32_t, std::string > >( R"({})"sv ) ==
|
||||
std::map< int32_t, std::string >{ } );
|
||||
CHECK( spb::json::deserialize< std::map< int32_t, std::string > >( R"(null)"sv ) ==
|
||||
std::map< int32_t, std::string >{ } );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< int32_t, std::string > >(
|
||||
R"()"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< int32_t, std::string > >(
|
||||
R"({"hello":"world"})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< int32_t, std::string > >(
|
||||
R"({"1":1})"sv ) );
|
||||
}
|
||||
SUBCASE( "string/int32" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::map< std::string, int32_t > >(
|
||||
R"({"hello":2})"sv ) ==
|
||||
std::map< std::string, int32_t >{ { "hello", 2 } } );
|
||||
CHECK( spb::json::deserialize< std::map< std::string, int32_t > >( R"({})"sv ) ==
|
||||
std::map< std::string, int32_t >{ } );
|
||||
CHECK( spb::json::deserialize< std::map< std::string, int32_t > >( R"(null)"sv ) ==
|
||||
std::map< std::string, int32_t >{ } );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< std::string, int32_t > >(
|
||||
R"()"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< std::string, int32_t > >(
|
||||
R"({"2","hello"})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< std::string, int32_t > >(
|
||||
R"({"1":})"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::map< std::string, int32_t > >(
|
||||
R"({"hello":2)"sv ) );
|
||||
}
|
||||
SUBCASE( "string/name" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::map< std::string, Test::Name > >(
|
||||
R"({"hello":{"name":"john"}})"sv ) ==
|
||||
std::map< std::string, Test::Name >{ { "hello", { .name = "john" } } } );
|
||||
}
|
||||
}
|
||||
SUBCASE( "string" )
|
||||
{
|
||||
SUBCASE( "escape" )
|
||||
{
|
||||
for( int c = 0; c <= 0xff; c++ )
|
||||
{
|
||||
const auto esc = c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' ||
|
||||
c == 'n' || c == 'r' || c == 't';
|
||||
|
||||
char buffer[] = { '"', '\\', char( c ), '"', 0 };
|
||||
if( !esc )
|
||||
{
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::string >(
|
||||
std::string_view( buffer ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
CHECK( spb::json::deserialize< std::string >( R"("hello")"sv ) == "hello" );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::string >( R"(hello")"sv ) );
|
||||
CHECK_THROWS( ( void ) spb::json::deserialize< std::string >( R"("hello)"sv ) );
|
||||
SUBCASE( "escaped" )
|
||||
{
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\"")"sv ) == "\"" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\\")"sv ) == "\\" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\/")"sv ) == "/" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\b")"sv ) == "\b" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\f")"sv ) == "\f" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\n")"sv ) == "\n" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\r")"sv ) == "\r" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\t")"sv ) == "\t" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\"\b\f\n\r\t\"")"sv ) ==
|
||||
"\"\b\f\n\r\t\"" );
|
||||
CHECK( spb::json::deserialize< std::string >( R"("\nhell\to")"sv ) ==
|
||||
"\nhell\to" );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "variant" )
|
||||
{
|
||||
SUBCASE( "int" )
|
||||
{
|
||||
auto variant = spb::json::deserialize< Test::Variant >( R"({"var_int":42})"sv );
|
||||
CHECK( variant.oneof_field.index( ) == 0 );
|
||||
CHECK( std::get< 0 >( variant.oneof_field ) == 42 );
|
||||
}
|
||||
SUBCASE( "string" )
|
||||
{
|
||||
auto variant =
|
||||
spb::json::deserialize< Test::Variant >( R"({"var_string":"hello"})"sv );
|
||||
CHECK( variant.oneof_field.index( ) == 1 );
|
||||
CHECK( std::get< 1 >( variant.oneof_field ) == std::string( "hello" ) );
|
||||
}
|
||||
SUBCASE( "bytes" )
|
||||
{
|
||||
auto variant =
|
||||
spb::json::deserialize< Test::Variant >( R"({"var_bytes":"aGVsbG8="})"sv );
|
||||
CHECK( variant.oneof_field.index( ) == 2 );
|
||||
CHECK( std::get< 2 >( variant.oneof_field ) ==
|
||||
std::vector< std::byte >{ std::byte( 'h' ), std::byte( 'e' ),
|
||||
std::byte( 'l' ), std::byte( 'l' ),
|
||||
std::byte( 'o' ) } );
|
||||
}
|
||||
SUBCASE( "name" )
|
||||
{
|
||||
auto variant =
|
||||
spb::json::deserialize< Test::Variant >( R"({"name":{"name":"John"}})"sv );
|
||||
CHECK( variant.oneof_field.index( ) == 3 );
|
||||
CHECK( std::get< 3 >( variant.oneof_field ) == Test::Name{ .name = "John" } );
|
||||
}
|
||||
SUBCASE( "collision" )
|
||||
{
|
||||
auto variant =
|
||||
spb::json::deserialize< Test::Variant >( R"({"bkfvdzz":{"name":"John"}})"sv );
|
||||
CHECK( variant.oneof_field.index( ) == 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
SUBCASE( "serialize" )
|
||||
{
|
||||
SUBCASE( "string" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "john" ) == R"("john")" );
|
||||
SUBCASE( "escaped" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\"" ) == R"("\"")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\\" ) == R"("\\")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "/" ) == R"("/")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\b" ) == R"("\b")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\f" ) == R"("\f")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\n" ) == R"("\n")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\r" ) == R"("\r")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\t" ) == R"("\t")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\"\\/\b\f\n\r\t" ) ==
|
||||
R"("\"\\/\b\f\n\r\t")" );
|
||||
CHECK( spb::json::serialize< std::string, std::string >( "\"hello\t" ) ==
|
||||
R"("\"hello\t")" );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::optional< std::string > >(
|
||||
std::nullopt ) == "" );
|
||||
CHECK( spb::json::serialize< std::string, std::optional< std::string > >(
|
||||
"hello" ) == R"("hello")" );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::string > >(
|
||||
{ "hello", "world" } ) == R"(["hello","world"])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::string > >( { } ) ==
|
||||
"" );
|
||||
}
|
||||
}
|
||||
SUBCASE( "bool" )
|
||||
{
|
||||
CHECK( spb::json::serialize( true ) == "true" );
|
||||
CHECK( spb::json::serialize( false ) == "false" );
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::optional< bool > >( std::nullopt ) ==
|
||||
"" );
|
||||
CHECK( spb::json::serialize< std::string, std::optional< bool > >( true ) ==
|
||||
"true" );
|
||||
CHECK( spb::json::serialize< std::string, std::optional< bool > >( false ) ==
|
||||
"false" );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::vector< bool > >(
|
||||
{ true, false } ) == R"([true,false])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< bool > >( { } ) == "" );
|
||||
}
|
||||
}
|
||||
SUBCASE( "int" )
|
||||
{
|
||||
CHECK( spb::json::serialize( 42 ) == "42" );
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::optional< int > >( std::nullopt ) ==
|
||||
"" );
|
||||
CHECK( spb::json::serialize< std::string, std::optional< int > >( 42 ) == "42" );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::vector< int > >( { 42 } ) ==
|
||||
R"([42])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< int > >( { 42, 3 } ) ==
|
||||
R"([42,3])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< int > >( { } ) == "" );
|
||||
}
|
||||
}
|
||||
SUBCASE( "double" )
|
||||
{
|
||||
CHECK( spb::json::serialize( 42.0 ) == "42" );
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::optional< double > >(
|
||||
std::nullopt ) == "" );
|
||||
CHECK( spb::json::serialize< std::string, std::optional< double > >( 42.3 ) ==
|
||||
"42.3" );
|
||||
}
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::vector< double > >( { 42.3 } ) ==
|
||||
R"([42.3])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< double > >(
|
||||
{ 42.3, 3.0 } ) == R"([42.3,3])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< double > >( { } ) == "" );
|
||||
}
|
||||
}
|
||||
SUBCASE( "bytes" )
|
||||
{
|
||||
CHECK( spb::json::serialize( to_bytes( "\x00\x01\x02"sv ) ) == R"("AAEC")" );
|
||||
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::byte > >(
|
||||
to_bytes( "\x00\x01\x02\x03\x04"sv ) ) == R"("AAECAwQ=")" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::byte > >(
|
||||
to_bytes( "hello"sv ) ) == R"("aGVsbG8=")" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::byte > >( { } ) == "" );
|
||||
|
||||
SUBCASE( "repeated" )
|
||||
{
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::vector< std::byte > > >(
|
||||
std::vector< std::vector< std::byte > >{
|
||||
to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) == R"(["AAECAwQ="])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::vector< std::byte > > >(
|
||||
std::vector< std::vector< std::byte > >{
|
||||
to_bytes( "\x00\x01\x02\x03\x04"sv ), to_bytes( "hello"sv ) } ) ==
|
||||
R"(["AAECAwQ=","aGVsbG8="])" );
|
||||
CHECK( spb::json::serialize< std::string, std::vector< std::vector< std::byte > > >(
|
||||
std::vector< std::vector< std::byte > >{ } ) == "" );
|
||||
}
|
||||
SUBCASE( "optional" )
|
||||
{
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::nullopt ) == "" );
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::vector< std::byte >{ to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) ==
|
||||
R"("AAECAwQ=")" );
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::vector< std::byte >{ to_bytes( "hello"sv ) } ) == R"("aGVsbG8=")" );
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::vector< std::byte >{ } ) == "" );
|
||||
}
|
||||
SUBCASE( "fixed" )
|
||||
{
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::nullopt ) == "" );
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::vector< std::byte >{ to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) ==
|
||||
R"("AAECAwQ=")" );
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::vector< std::byte >{ to_bytes( "hello"sv ) } ) == R"("aGVsbG8=")" );
|
||||
CHECK(
|
||||
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
|
||||
std::vector< std::byte >{ } ) == "" );
|
||||
}
|
||||
}
|
||||
SUBCASE( "variant" )
|
||||
{
|
||||
SUBCASE( "int" )
|
||||
{
|
||||
CHECK( spb::json::serialize( Test::Variant{ .oneof_field = 42U } ) ==
|
||||
R"({"var_int":42})" );
|
||||
}
|
||||
SUBCASE( "string" )
|
||||
{
|
||||
CHECK( spb::json::serialize( Test::Variant{ .oneof_field = "hello" } ) ==
|
||||
R"({"var_string":"hello"})" );
|
||||
}
|
||||
SUBCASE( "bytes" )
|
||||
{
|
||||
CHECK(
|
||||
spb::json::serialize( Test::Variant{
|
||||
.oneof_field = std::vector< std::byte >{
|
||||
std::byte( 'h' ), std::byte( 'e' ), std::byte( 'l' ), std::byte( 'l' ),
|
||||
std::byte( 'o' ) } } ) == R"({"var_bytes":"aGVsbG8="})" );
|
||||
}
|
||||
SUBCASE( "name" )
|
||||
{
|
||||
CHECK( spb::json::serialize(
|
||||
Test::Variant{ .oneof_field = Test::Name{ .name = "John" } } ) ==
|
||||
R"({"name":{"name":"John"}})" );
|
||||
}
|
||||
}
|
||||
SUBCASE( "map" )
|
||||
{
|
||||
SUBCASE( "int32/int32" )
|
||||
{
|
||||
CHECK( spb::json::serialize( std::map< int32_t, int32_t >{ { 1, 2 } } ) ==
|
||||
R"({"1":2})" );
|
||||
CHECK( spb::json::serialize( std::map< int32_t, int32_t >{ { 1, 2 }, { 2, 3 } } ) ==
|
||||
R"({"1":2,"2":3})" );
|
||||
CHECK( spb::json::serialize( std::map< int32_t, int32_t >{ } ) == "" );
|
||||
}
|
||||
SUBCASE( "string/string" )
|
||||
{
|
||||
CHECK( spb::json::serialize( std::map< std::string, std::string >{
|
||||
{ "hello", "world" } } ) == R"({"hello":"world"})" );
|
||||
CHECK( spb::json::serialize( std::map< std::string, std::string >{ } ) == "" );
|
||||
}
|
||||
SUBCASE( "int32/string" )
|
||||
{
|
||||
CHECK( spb::json::serialize( std::map< int32_t, std::string >{ { 1, "hello" } } ) ==
|
||||
( R"({"1":"hello"})" ) );
|
||||
CHECK( spb::json::serialize( std::map< int32_t, std::string >{ } ) == "" );
|
||||
}
|
||||
SUBCASE( "string/int32" )
|
||||
{
|
||||
CHECK( spb::json::serialize( std::map< std::string, int32_t >{ { "hello", 2 } } ) ==
|
||||
R"({"hello":2})" );
|
||||
CHECK( spb::json::serialize( std::map< std::string, int32_t >{ } ) == "" );
|
||||
}
|
||||
SUBCASE( "string/name" )
|
||||
{
|
||||
CHECK( spb::json::serialize( std::map< std::string, Test::Name >{
|
||||
{ "hello", { .name = "john" } } } ) == R"({"hello":{"name":"john"}})" );
|
||||
}
|
||||
}
|
||||
SUBCASE( "enum" )
|
||||
{
|
||||
CHECK( spb::json::serialize( PhoneBook::Person::PhoneType::HOME ) == "\"HOME\"" );
|
||||
}
|
||||
SUBCASE( "person" )
|
||||
{
|
||||
CHECK( spb::json::serialize( PhoneBook::Person{
|
||||
.name = "John Doe",
|
||||
.id = 123,
|
||||
.email = "QXUeh@example.com",
|
||||
.phones = {
|
||||
PhoneBook::Person::PhoneNumber{
|
||||
.number = "555-4321",
|
||||
.type = PhoneBook::Person::PhoneType::HOME,
|
||||
},
|
||||
},
|
||||
} ) == R"({"name":"John Doe","id":123,"email":"QXUeh@example.com","phones":[{"number":"555-4321","type":"HOME"}]})" );
|
||||
}
|
||||
SUBCASE( "name" )
|
||||
{
|
||||
CHECK( spb::json::serialize( Test::Name{ } ) == R"({})" );
|
||||
CHECK( spb::json::serialize_size( Test::Name{ } ) == 2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
558
3rdparty/simple-protobuf/test/parser.cpp
vendored
Normal file
558
3rdparty/simple-protobuf/test/parser.cpp
vendored
Normal file
@ -0,0 +1,558 @@
|
||||
#include "ast/ast.h"
|
||||
#include "io/file.h"
|
||||
#include <cstdlib>
|
||||
#include <dumper/dumper.h>
|
||||
#include <filesystem>
|
||||
#include <parser/parser.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
struct proto_file_test
|
||||
{
|
||||
const std::string_view file_content;
|
||||
size_t error_line = 0;
|
||||
};
|
||||
|
||||
void test_proto_file( const proto_file_test & test,
|
||||
const std::string & test_file = "tmp_test.proto",
|
||||
std::span< const std::filesystem::path > import_paths = { } )
|
||||
{
|
||||
REQUIRE_NOTHROW( save_file( test_file, test.file_content ) );
|
||||
try
|
||||
{
|
||||
auto file = parse_proto_file( test_file, import_paths );
|
||||
auto stream = std::stringstream( );
|
||||
dump_cpp_header( file, stream );
|
||||
dump_cpp( file, "header.pb.h", stream );
|
||||
REQUIRE( test.error_line == 0 );
|
||||
}
|
||||
catch( const std::exception & ex )
|
||||
{
|
||||
auto message = std::string_view( ex.what( ) );
|
||||
REQUIRE( message.find( test_file + ":" ) != message.npos );
|
||||
message.remove_prefix( message.find( test_file + ":" ) + 1 + test_file.size( ) );
|
||||
auto line = std::strtoul( message.data( ), nullptr, 10 );
|
||||
REQUIRE( test.error_line == line );
|
||||
}
|
||||
REQUIRE_NOTHROW( std::filesystem::remove( test_file ) );
|
||||
}
|
||||
|
||||
void test_files( std::span< const proto_file_test > files,
|
||||
const std::string & test_file = "tmp_test.proto",
|
||||
std::span< const std::filesystem::path > import_paths = { } )
|
||||
{
|
||||
for( const auto & file : files )
|
||||
{
|
||||
test_proto_file( file, test_file, import_paths );
|
||||
}
|
||||
}
|
||||
}// namespace
|
||||
|
||||
TEST_CASE( "protoc-parser" )
|
||||
{
|
||||
SUBCASE( "api" )
|
||||
{
|
||||
REQUIRE_THROWS( parse_proto_file( "not_found.proto", { { "../", ".", "/home/" } } ) );
|
||||
REQUIRE( cpp_file_name_from_proto( "messages.proto", ".cpp" ) == "messages.cpp" );
|
||||
}
|
||||
SUBCASE( "import" )
|
||||
{
|
||||
REQUIRE_NOTHROW( std::filesystem::create_directories( "level1/level2/level3" ) );
|
||||
|
||||
REQUIRE_NOTHROW( save_file( "empty.proto", "" ) );
|
||||
REQUIRE_NOTHROW( save_file( "level1/empty1.proto", "import \"level2/empty2.proto\";" ) );
|
||||
REQUIRE_NOTHROW(
|
||||
save_file( "level1/level2/empty2.proto", "import \"level3/empty3.proto\";" ) );
|
||||
REQUIRE_NOTHROW( save_file( "level1/level2/level3/empty3.proto", "" ) );
|
||||
REQUIRE_NOTHROW( save_file( "level1.proto", "import \"level1/empty1.proto\";" ) );
|
||||
REQUIRE_NOTHROW( save_file( "level2.proto", "import \"level1/level2/empty2.proto\";" ) );
|
||||
REQUIRE_NOTHROW(
|
||||
save_file( "level3.proto", "import \"level1/level2/level3/empty3.proto\";" ) );
|
||||
REQUIRE_NOTHROW( save_file( "level2-from-1.proto", "import \"level2/empty2.proto\";" ) );
|
||||
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(package UnitTest;
|
||||
import "empty.proto";
|
||||
)",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
import "level1.proto";
|
||||
import "level2.proto";
|
||||
import "level3.proto";
|
||||
)",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
import "empty_not_found.proto";
|
||||
)",
|
||||
2 },
|
||||
{ R"(package UnitTest;
|
||||
import "level2-from-1.proto";
|
||||
)",
|
||||
2 },
|
||||
{ R"(package UnitTest;
|
||||
importX "message.proto";
|
||||
)",
|
||||
2 },
|
||||
};
|
||||
test_files( tests );
|
||||
constexpr proto_file_test tests2[] = {
|
||||
{ R"(package UnitTest;
|
||||
import "level2-from-1.proto";
|
||||
)",
|
||||
0 },
|
||||
};
|
||||
|
||||
test_files( tests2, "tmp_test.proto", { { "level2", "level1" } } );
|
||||
test_files( tests2, "tmp_test.proto", { { std::filesystem::current_path( ) / "level1" } } );
|
||||
test_files( tests2, ( std::filesystem::current_path( ) / "tmp_test.proto" ).string( ),
|
||||
{ { "level2", "level1" } } );
|
||||
|
||||
REQUIRE_NOTHROW( std::filesystem::remove( "empty.proto" ) );
|
||||
REQUIRE_NOTHROW( std::filesystem::remove( "level1.proto" ) );
|
||||
REQUIRE_NOTHROW( std::filesystem::remove( "level2.proto" ) );
|
||||
REQUIRE_NOTHROW( std::filesystem::remove( "level3.proto" ) );
|
||||
REQUIRE_NOTHROW( std::filesystem::remove_all( "level1" ) );
|
||||
}
|
||||
SUBCASE( "comment" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = { { "//"sv, 1 },
|
||||
{ "/*"sv, 1 },
|
||||
{ "/-"sv, 1 },
|
||||
{ R"(package UnitTest;
|
||||
// comment
|
||||
)",
|
||||
0 },
|
||||
{ "/**/"sv, 0 },
|
||||
{ R"(package UnitTest;
|
||||
/* comment
|
||||
*/;
|
||||
)",
|
||||
0 },
|
||||
{ R"(syntax = "proto3";
|
||||
message Message
|
||||
{
|
||||
required uint32 value = 1; // comment
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(syntax = "proto3";
|
||||
message Message
|
||||
{
|
||||
// comment
|
||||
required uint32 value = 1;
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(syntax = "proto3";
|
||||
message Message
|
||||
{
|
||||
// comment
|
||||
required uint32 value = 1; // comment
|
||||
})"sv,
|
||||
0 } };
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "syntax" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = { { ""sv, 0 },
|
||||
{ "X"sv, 1 },
|
||||
{ R"(synta = "proto2";)"sv, 1 },
|
||||
{ R"(syntax = "proto1;")"sv, 1 },
|
||||
{ R"(syntax = "proto2")"sv, 1 },
|
||||
{ R"(syntax = "proto2";)"sv, 0 },
|
||||
{ R"(syntax = "proto3";;)"sv, 0 },
|
||||
{ R"(syntax = "proto3")"sv, 1 },
|
||||
{ R"(syntax = "proto4;")"sv, 1 },
|
||||
{ R"(syntax = "proto2";
|
||||
message Message
|
||||
{
|
||||
required uint32 value = 1;
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(syntax = "proto3";
|
||||
message Message
|
||||
{
|
||||
required uint32 value = 1;
|
||||
})"sv,
|
||||
0 } };
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "scalar" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(message Message{
|
||||
bool b = 1;
|
||||
float f = 2;
|
||||
double d = 3;
|
||||
int32 i32 = 4;
|
||||
sint32 si32 = 5;
|
||||
uint32 u32 = 6;
|
||||
int64 i64 = 7;
|
||||
sint64 si64 = 8;
|
||||
uint64 u64 = 9;
|
||||
fixed32 f32 = 10;
|
||||
sfixed32 sf32 = 11;
|
||||
fixed64 f64 = 12;
|
||||
sfixed64 sf64 = 13;
|
||||
string s = 14;
|
||||
bytes by = 15;
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(message Message{
|
||||
repeated bool b = 1;
|
||||
repeated float f = 2;
|
||||
repeated double d = 3;
|
||||
repeated int32 i32 = 4;
|
||||
repeated sint32 si32 = 5;
|
||||
repeated uint32 u32 = 6;
|
||||
repeated int64 i64 = 7;
|
||||
repeated sint64 si64 = 8;
|
||||
repeated uint64 u64 = 9;
|
||||
repeated fixed32 f32 = 10;
|
||||
repeated sfixed32 sf32 = 11;
|
||||
repeated fixed64 f64 = 12;
|
||||
repeated sfixed64 sf64 = 13;
|
||||
repeated string s = 14;
|
||||
repeated bytes by = 15;
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(message Message{
|
||||
repeated bool b = 1 [packed = true];
|
||||
repeated float f = 2 [packed = true];
|
||||
repeated double d = 3 [packed = true];
|
||||
repeated int32 i32 = 4 [packed = true];
|
||||
repeated sint32 si32 = 5 [packed = true];
|
||||
repeated uint32 u32 = 6 [packed = true];
|
||||
repeated int64 i64 = 7 [packed = true];
|
||||
repeated sint64 si64 = 8 [packed = true];
|
||||
repeated uint64 u64 = 9 [packed = true];
|
||||
repeated fixed32 f32 = 10 [packed = true];
|
||||
repeated sfixed32 sf32 = 11 [packed = true];
|
||||
repeated fixed64 f64 = 12 [packed = true];
|
||||
repeated sfixed64 sf64 = 13 [packed = true];
|
||||
repeated string s = 14 [packed = true];
|
||||
repeated bytes by = 15 [packed = true];
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(message Message{
|
||||
bool b = 1 [default = true];
|
||||
float f = 2 [default = 0.4];
|
||||
double d = 3 [default = 0.];
|
||||
int32 i32 = 4 [default = -2];
|
||||
sint32 si32 = 5 [default = -3];
|
||||
uint32 u32 = 6 [default = 5];
|
||||
int64 i64 = 7 [default = 7];
|
||||
sint64 si64 = 8 [default = 8];
|
||||
uint64 u64 = 9 [default = 6];
|
||||
fixed32 f32 = 10 [default = 3];
|
||||
sfixed32 sf32 = 11 [default = 6];
|
||||
fixed64 f64 = 12 [default = 3];
|
||||
sfixed64 sf64 = 13 [default = 4];
|
||||
string s = 14 [default = "true"];
|
||||
bytes by = 15;
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(message Message{
|
||||
bool b = X;
|
||||
})"sv,
|
||||
2 },
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "option" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = { { R"(package UnitTest;
|
||||
option cc_enable_arenas true;
|
||||
)",
|
||||
2 },
|
||||
{ R"(package UnitTest;
|
||||
option cc_enable_arenas = true;
|
||||
)",
|
||||
0 },
|
||||
{ R"(syntax = "proto3";
|
||||
message Message
|
||||
{
|
||||
required uint32 value = 1 [default = 2, deprecated = true];
|
||||
})"sv,
|
||||
0 } };
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "extensions" )
|
||||
{
|
||||
SUBCASE( "enum type" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = { { R"(syntax = "proto2";
|
||||
//[[enum.type = "float"]]
|
||||
enum Enum {
|
||||
value = 1;
|
||||
})"sv,
|
||||
2 }
|
||||
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "bitfield" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(syntax = "proto2";
|
||||
message ReqUint8_1{
|
||||
//[[ field.type = "uint8:1" ]]
|
||||
required uint32 value = 1;
|
||||
//[[ field.type = "int8:1" ]]
|
||||
required sint32 value2 = 2;
|
||||
//[[ field.type = "int8:1" ]]
|
||||
required sint64 value3 = 3;
|
||||
//[[ field.type = "uint8:1" ]]
|
||||
required fixed32 value4 = 4;
|
||||
//[[ field.type = "int8:1" ]]
|
||||
required sfixed32 value5 = 5;
|
||||
//[[ field.type = "int8:1" ]]
|
||||
required sfixed64 value6 = 6;
|
||||
//[[ field.type = "uint8:1" ]]
|
||||
required fixed64 value7 = 7;
|
||||
})"sv,
|
||||
0 },
|
||||
{ R"(syntax = "proto2";
|
||||
message OptUint8_1{
|
||||
//[[ field.type = "uint8:1" ]]
|
||||
optional uint32 value = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(syntax = "proto2";
|
||||
message OptUint8_1{
|
||||
//[[ field.type = "uint8:1" ]]
|
||||
required bool value = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(syntax = "proto2";
|
||||
message OptUint8_1{
|
||||
//[[ field.type = "uint8:1" ]]
|
||||
required float value = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(syntax = "proto2";
|
||||
message OptUint8_1{
|
||||
//[[ field.type = "uint8:1" ]]
|
||||
required double value = 1;
|
||||
})",
|
||||
3 },
|
||||
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "small int" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
// [[ field.type = "uint8" ]]
|
||||
optional uint32 u32 = 1;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
// [[ field.type = "uint16" ]]
|
||||
optional uint32 u32 = 1;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
// [[ field.type = "uint32" ]]
|
||||
optional uint32 u32 = 1;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
// [[ field.type = "uint64" ]]
|
||||
optional uint32 u32 = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(syntax = "proto2";
|
||||
message ReqUint8_1{
|
||||
//[[ field.type = "int8" ]]
|
||||
required uint32 value = 1;
|
||||
})"sv,
|
||||
3 },
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
}
|
||||
SUBCASE( "dependency" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
optional A a = 1;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
repeated A a = 1;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
required A a = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
required B b = 1;
|
||||
}
|
||||
message B {
|
||||
required A a = 1;
|
||||
})",
|
||||
2 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
message B {
|
||||
required A a = 1;
|
||||
}
|
||||
required B b = 1;
|
||||
})",
|
||||
4 },
|
||||
{ R"(package UnitTest;
|
||||
option cc_enable_arenas = true;
|
||||
message A {
|
||||
message B {
|
||||
repeated A a = 1;
|
||||
required bool b = 2 [ default = true ];
|
||||
map<int32, int32> map_field = 3;
|
||||
}
|
||||
required B b = 1;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest.dependency;
|
||||
message A {
|
||||
message B { optional C c = 1; }
|
||||
message C { optional int32 b = 1; }
|
||||
message D { optional A a = 1; }
|
||||
message E {
|
||||
repeated F f = 1;
|
||||
optional int32 b = 2 [default = -2];
|
||||
}
|
||||
message F {
|
||||
repeated E e = 1;
|
||||
optional int32 c = 2;
|
||||
}
|
||||
message G {
|
||||
optional H h = 1;
|
||||
optional int32 b = 2;
|
||||
}
|
||||
message H {
|
||||
optional G g = 1;
|
||||
optional int32 c = 2 [default = 2];
|
||||
}
|
||||
optional int32 a = 1;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
oneof oneof_field {
|
||||
uint32 oneof_uint32 = 1;
|
||||
string oneof_string = 2;
|
||||
bytes oneof_bytes = 3;
|
||||
A a = 4;
|
||||
}
|
||||
})",
|
||||
2 },
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "oneof" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
oneof oneof_field {
|
||||
uint32 oneof_uint32 = 1;
|
||||
string oneof_string = 2;
|
||||
bytes oneof_bytes = 3;
|
||||
}
|
||||
})",
|
||||
0 },
|
||||
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "reserved" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
reserved 4;
|
||||
reserved 5,6,7;
|
||||
reserved 8 to 10;
|
||||
reserved 10 to max;
|
||||
reserved "BB";
|
||||
})",
|
||||
0 },
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "enum" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
})",
|
||||
0 },
|
||||
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
SUBCASE( "map" )
|
||||
{
|
||||
constexpr proto_file_test tests[] = {
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
map<int32, int32> m_int32 = 1;
|
||||
map<int64, int64> m_int64 = 2;
|
||||
map<uint32, uint32> m_uint32 = 3;
|
||||
map<uint64, uint64> m_uint64 = 4;
|
||||
map<sint32, sint32> m_sint32 = 5;
|
||||
map<sint64, sint64> m_sint64 = 6;
|
||||
map<fixed32, fixed32> m_fixed32 = 7;
|
||||
map<fixed64, fixed64> m_fixed64 = 8;
|
||||
map<sfixed32, sfixed32> m_sfixed32 = 9;
|
||||
map<sfixed64, sfixed64> m_sfixed64 = 10;
|
||||
map<bool, bool> m_bool = 11;
|
||||
map<string, bool> m_string = 12;
|
||||
})",
|
||||
0 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
map<float, int32> m_int32 = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
map<double, int32> m_int32 = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
map<bytes, int32> m_int32 = 1;
|
||||
})",
|
||||
3 },
|
||||
{ R"(package UnitTest;
|
||||
message A {
|
||||
map<X, int32> m_int32 = 1;
|
||||
})",
|
||||
3 },
|
||||
|
||||
};
|
||||
test_files( tests );
|
||||
}
|
||||
}
|
||||
2109
3rdparty/simple-protobuf/test/pb.cpp
vendored
Normal file
2109
3rdparty/simple-protobuf/test/pb.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
26
3rdparty/simple-protobuf/test/proto/dependency.proto
vendored
Normal file
26
3rdparty/simple-protobuf/test/proto/dependency.proto
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package UnitTest.dependency;
|
||||
|
||||
message A {
|
||||
message B { optional C c = 1; }
|
||||
message C { optional int32 b = 1; }
|
||||
message D { optional A a = 1; }
|
||||
message E {
|
||||
repeated F f = 1;
|
||||
optional int32 b = 2;
|
||||
}
|
||||
message F {
|
||||
repeated E e = 1;
|
||||
optional int32 c = 2;
|
||||
}
|
||||
message G {
|
||||
optional H h = 1;
|
||||
optional int32 b = 2;
|
||||
}
|
||||
message H {
|
||||
optional G g = 1;
|
||||
optional int32 c = 2;
|
||||
}
|
||||
optional int32 a = 1;
|
||||
}
|
||||
1249
3rdparty/simple-protobuf/test/proto/desc.proto
vendored
Normal file
1249
3rdparty/simple-protobuf/test/proto/desc.proto
vendored
Normal file
File diff suppressed because it is too large
Load Diff
14
3rdparty/simple-protobuf/test/proto/enum.proto
vendored
Normal file
14
3rdparty/simple-protobuf/test/proto/enum.proto
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package example;
|
||||
|
||||
enum SomeEnum {
|
||||
SOME_ENUM_UNDEFINED = 0;
|
||||
SOME_ENUM_ONE = 1;
|
||||
SOME_ENUM_TWO = 2;
|
||||
SOME_ENUM_THREE = 3;
|
||||
SOME_ENUM_FOUR = 4;
|
||||
SOME_ENUM_FIVE = 5;
|
||||
};
|
||||
|
||||
message SomeMessage { repeated SomeEnum values = 1; };
|
||||
31
3rdparty/simple-protobuf/test/proto/etl/etl-person.proto.in
vendored
Normal file
31
3rdparty/simple-protobuf/test/proto/etl/etl-person.proto.in
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
syntax = "proto2";
|
||||
|
||||
//[[ repeated.type = "etl::vector<$,60>"]]
|
||||
//[[ repeated.include = "<etl/vector.h>"]]
|
||||
|
||||
//[[ string.type = "etl::string<32>"]]
|
||||
//[[ string.include = "<etl/string.h>"]]
|
||||
|
||||
package PhoneBook.Etl@PB_PACKAGE@;
|
||||
|
||||
message Person {
|
||||
//[[ string.type = "std::string"]]
|
||||
optional string name = 1;
|
||||
optional int32 id = 2; // Unique ID number for this person.
|
||||
//[[ string.type = "etl::string<64>"]]
|
||||
optional string email = 3;
|
||||
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
|
||||
//[[ string.type = "etl::string<16>"]]
|
||||
message PhoneNumber {
|
||||
required string number = 1; // phone number is always required
|
||||
optional PhoneType type = 2;
|
||||
}
|
||||
// all registered phones
|
||||
repeated PhoneNumber phones = 4;
|
||||
}
|
||||
283
3rdparty/simple-protobuf/test/proto/etl/etl-scalar.proto.in
vendored
Normal file
283
3rdparty/simple-protobuf/test/proto/etl/etl-scalar.proto.in
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
syntax = "proto2";
|
||||
|
||||
//[[ repeated.type = "etl::vector<$,64>"]]
|
||||
//[[ repeated.include = "<etl/vector.h>"]]
|
||||
|
||||
//[[ bytes.type = "etl::vector<$,8>"]]
|
||||
//[[ bytes.include = "<etl/vector.h>"]]
|
||||
|
||||
//[[ string.type = "etl::string<16>"]]
|
||||
//[[ string.include = "<etl/string.h>"]]
|
||||
|
||||
package Test.Etl.Scalar@PB_PACKAGE@;
|
||||
|
||||
message Simple
|
||||
{
|
||||
optional string value = 100;
|
||||
}
|
||||
|
||||
message OptInt32
|
||||
{
|
||||
optional int32 value = 1;
|
||||
}
|
||||
message ReqInt32
|
||||
{
|
||||
required int32 value = 1;
|
||||
}
|
||||
message RepInt32
|
||||
{
|
||||
repeated int32 value = 1;
|
||||
}
|
||||
message RepPackInt32
|
||||
{
|
||||
repeated int32 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptInt64
|
||||
{
|
||||
optional int64 value = 1;
|
||||
}
|
||||
message ReqInt64
|
||||
{
|
||||
required int64 value = 1;
|
||||
}
|
||||
message RepInt64
|
||||
{
|
||||
repeated int64 value = 1;
|
||||
}
|
||||
message RepPackInt64
|
||||
{
|
||||
repeated int64 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptUint32
|
||||
{
|
||||
optional uint32 value = 1;
|
||||
}
|
||||
message ReqUint32
|
||||
{
|
||||
required uint32 value = 1;
|
||||
}
|
||||
message RepUint32
|
||||
{
|
||||
repeated uint32 value = 1;
|
||||
}
|
||||
message RepPackUint32
|
||||
{
|
||||
repeated uint32 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptUint64
|
||||
{
|
||||
optional uint64 value = 1;
|
||||
}
|
||||
message ReqUint64
|
||||
{
|
||||
required uint64 value = 1;
|
||||
}
|
||||
message RepUint64
|
||||
{
|
||||
repeated uint64 value = 1;
|
||||
}
|
||||
message RepPackUint64
|
||||
{
|
||||
repeated uint64 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptSint32
|
||||
{
|
||||
optional sint32 value = 1;
|
||||
}
|
||||
message ReqSint32
|
||||
{
|
||||
required sint32 value = 1;
|
||||
}
|
||||
message RepSint32
|
||||
{
|
||||
repeated sint32 value = 1;
|
||||
}
|
||||
message RepPackSint32
|
||||
{
|
||||
repeated sint32 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptSint64
|
||||
{
|
||||
optional sint64 value = 1;
|
||||
}
|
||||
message ReqSint64
|
||||
{
|
||||
required sint64 value = 1;
|
||||
}
|
||||
message RepSint64
|
||||
{
|
||||
repeated sint64 value = 1;
|
||||
}
|
||||
message RepPackSint64
|
||||
{
|
||||
repeated sint64 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptFixed32
|
||||
{
|
||||
optional fixed32 value = 1;
|
||||
}
|
||||
message ReqFixed32
|
||||
{
|
||||
required fixed32 value = 1;
|
||||
}
|
||||
message RepFixed32
|
||||
{
|
||||
repeated fixed32 value = 1;
|
||||
}
|
||||
message RepPackFixed32
|
||||
{
|
||||
repeated fixed32 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptFixed64
|
||||
{
|
||||
optional fixed64 value = 1;
|
||||
}
|
||||
message ReqFixed64
|
||||
{
|
||||
required fixed64 value = 1;
|
||||
}
|
||||
message RepFixed64
|
||||
{
|
||||
repeated fixed64 value = 1;
|
||||
}
|
||||
message RepPackFixed64
|
||||
{
|
||||
repeated fixed64 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptSfixed32
|
||||
{
|
||||
optional sfixed32 value = 1;
|
||||
}
|
||||
message ReqSfixed32
|
||||
{
|
||||
required sfixed32 value = 1;
|
||||
}
|
||||
message RepSfixed32
|
||||
{
|
||||
repeated sfixed32 value = 1;
|
||||
}
|
||||
message RepPackSfixed32
|
||||
{
|
||||
repeated sfixed32 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptSfixed64
|
||||
{
|
||||
optional sfixed64 value = 1;
|
||||
}
|
||||
message ReqSfixed64
|
||||
{
|
||||
required sfixed64 value = 1;
|
||||
}
|
||||
message RepSfixed64
|
||||
{
|
||||
repeated sfixed64 value = 1;
|
||||
}
|
||||
message RepPackSfixed64
|
||||
{
|
||||
repeated sfixed64 value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptBool
|
||||
{
|
||||
optional bool value = 1;
|
||||
}
|
||||
message ReqBool
|
||||
{
|
||||
required bool value = 1;
|
||||
}
|
||||
message RepBool
|
||||
{
|
||||
repeated bool value = 1;
|
||||
}
|
||||
|
||||
message RepPackBool
|
||||
{
|
||||
repeated bool value = 1 [ packed = true ];
|
||||
}
|
||||
|
||||
message OptFloat
|
||||
{
|
||||
optional float value = 1;
|
||||
}
|
||||
message ReqFloat
|
||||
{
|
||||
required float value = 1;
|
||||
}
|
||||
message RepFloat
|
||||
{
|
||||
repeated float value = 1;
|
||||
}
|
||||
|
||||
message OptDouble
|
||||
{
|
||||
optional double value = 1;
|
||||
}
|
||||
message ReqDouble
|
||||
{
|
||||
required double value = 1;
|
||||
}
|
||||
message RepDouble
|
||||
{
|
||||
repeated double value = 1;
|
||||
}
|
||||
|
||||
message OptString
|
||||
{
|
||||
optional string value = 1;
|
||||
}
|
||||
message ReqString
|
||||
{
|
||||
required string value = 1;
|
||||
}
|
||||
message RepString
|
||||
{
|
||||
repeated string value = 1;
|
||||
}
|
||||
|
||||
message OptStringView
|
||||
{
|
||||
optional string value = 1 [ctype = STRING_PIECE];
|
||||
}
|
||||
message ReqStringView
|
||||
{
|
||||
required string value = 1 [ctype = STRING_PIECE];
|
||||
}
|
||||
message RepStringView
|
||||
{
|
||||
repeated string value = 1 [ctype = STRING_PIECE];
|
||||
}
|
||||
|
||||
message OptBytes
|
||||
{
|
||||
optional bytes value = 1;
|
||||
}
|
||||
message ReqBytes
|
||||
{
|
||||
required bytes value = 1;
|
||||
}
|
||||
message RepBytes
|
||||
{
|
||||
repeated bytes value = 1;
|
||||
}
|
||||
|
||||
message OptBytesView
|
||||
{
|
||||
optional bytes value = 1 [ctype = STRING_PIECE];
|
||||
}
|
||||
message ReqBytesView
|
||||
{
|
||||
required bytes value = 1 [ctype = STRING_PIECE];
|
||||
}
|
||||
message RepBytesView
|
||||
{
|
||||
repeated bytes value = 1 [ctype = STRING_PIECE];
|
||||
}
|
||||
97
3rdparty/simple-protobuf/test/proto/map.proto
vendored
Normal file
97
3rdparty/simple-protobuf/test/proto/map.proto
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
option cc_enable_arenas = true;
|
||||
|
||||
// We don't put this in a package within proto2 because we need to make sure
|
||||
// that the generated code doesn't depend on being in the proto2 namespace.
|
||||
// In map_test_util.h we do "using namespace unittest = protobuf_unittest".
|
||||
package protobuf_unittest;
|
||||
|
||||
enum Proto2MapEnum {
|
||||
PROTO2_MAP_ENUM_FOO = 0;
|
||||
PROTO2_MAP_ENUM_BAR = 1;
|
||||
PROTO2_MAP_ENUM_BAZ = 2;
|
||||
}
|
||||
|
||||
enum Proto2MapEnumPlusExtra {
|
||||
E_PROTO2_MAP_ENUM_FOO = 0;
|
||||
E_PROTO2_MAP_ENUM_BAR = 1;
|
||||
E_PROTO2_MAP_ENUM_BAZ = 2;
|
||||
E_PROTO2_MAP_ENUM_EXTRA = 3;
|
||||
}
|
||||
|
||||
message TestEnumMap {
|
||||
map<int32, Proto2MapEnum> known_map_field = 101;
|
||||
map<int32, Proto2MapEnum> unknown_map_field = 102;
|
||||
|
||||
// Other maps with all key types to test the unknown entry serialization
|
||||
map<int64, Proto2MapEnum> unknown_map_field_int64 = 200;
|
||||
map<uint64, Proto2MapEnum> unknown_map_field_uint64 = 201;
|
||||
map<int32, Proto2MapEnum> unknown_map_field_int32 = 202;
|
||||
map<uint32, Proto2MapEnum> unknown_map_field_uint32 = 203;
|
||||
map<fixed32, Proto2MapEnum> unknown_map_field_fixed32 = 204;
|
||||
map<fixed64, Proto2MapEnum> unknown_map_field_fixed64 = 205;
|
||||
map<bool, Proto2MapEnum> unknown_map_field_bool = 206;
|
||||
map<string, Proto2MapEnum> unknown_map_field_string = 207;
|
||||
map<sint32, Proto2MapEnum> unknown_map_field_sint32 = 208;
|
||||
map<sint64, Proto2MapEnum> unknown_map_field_sint64 = 209;
|
||||
map<sfixed32, Proto2MapEnum> unknown_map_field_sfixed32 = 210;
|
||||
map<sfixed64, Proto2MapEnum> unknown_map_field_sfixed64 = 211;
|
||||
}
|
||||
|
||||
message TestEnumMapPlusExtra {
|
||||
map<int32, Proto2MapEnumPlusExtra> known_map_field = 101;
|
||||
map<int32, Proto2MapEnumPlusExtra> unknown_map_field = 102;
|
||||
|
||||
// Other maps with all key types to test the unknown entry serialization
|
||||
map<int64, Proto2MapEnumPlusExtra> unknown_map_field_int64 = 200;
|
||||
map<uint64, Proto2MapEnumPlusExtra> unknown_map_field_uint64 = 201;
|
||||
map<int32, Proto2MapEnumPlusExtra> unknown_map_field_int32 = 202;
|
||||
map<uint32, Proto2MapEnumPlusExtra> unknown_map_field_uint32 = 203;
|
||||
map<fixed32, Proto2MapEnumPlusExtra> unknown_map_field_fixed32 = 204;
|
||||
map<fixed64, Proto2MapEnumPlusExtra> unknown_map_field_fixed64 = 205;
|
||||
map<bool, Proto2MapEnumPlusExtra> unknown_map_field_bool = 206;
|
||||
map<string, Proto2MapEnumPlusExtra> unknown_map_field_string = 207;
|
||||
map<sint32, Proto2MapEnumPlusExtra> unknown_map_field_sint32 = 208;
|
||||
map<sint64, Proto2MapEnumPlusExtra> unknown_map_field_sint64 = 209;
|
||||
map<sfixed32, Proto2MapEnumPlusExtra> unknown_map_field_sfixed32 = 210;
|
||||
map<sfixed64, Proto2MapEnumPlusExtra> unknown_map_field_sfixed64 = 211;
|
||||
}
|
||||
|
||||
message TestIntIntMap { map<int32, int32> m = 1; }
|
||||
|
||||
// Test all key types: string, plus the non-floating-point scalars.
|
||||
message TestMaps {
|
||||
map<int32, TestIntIntMap> m_int32 = 1;
|
||||
map<int64, TestIntIntMap> m_int64 = 2;
|
||||
map<uint32, TestIntIntMap> m_uint32 = 3;
|
||||
map<uint64, TestIntIntMap> m_uint64 = 4;
|
||||
map<sint32, TestIntIntMap> m_sint32 = 5;
|
||||
map<sint64, TestIntIntMap> m_sint64 = 6;
|
||||
map<fixed32, TestIntIntMap> m_fixed32 = 7;
|
||||
map<fixed64, TestIntIntMap> m_fixed64 = 8;
|
||||
map<sfixed32, TestIntIntMap> m_sfixed32 = 9;
|
||||
map<sfixed64, TestIntIntMap> m_sfixed64 = 10;
|
||||
map<bool, TestIntIntMap> m_bool = 11;
|
||||
map<string, TestIntIntMap> m_string = 12;
|
||||
}
|
||||
|
||||
// Test maps in submessages.
|
||||
message TestSubmessageMaps { optional TestMaps m = 1; }
|
||||
|
||||
message TestProto2BytesMap {
|
||||
map<int32, bytes> map_bytes = 1;
|
||||
map<int32, string> map_string = 2;
|
||||
}
|
||||
|
||||
message TestProto2FloatMap {
|
||||
map<int32, float> map_floats = 1;
|
||||
map<int32, double> map_doubles = 2;
|
||||
}
|
||||
4062
3rdparty/simple-protobuf/test/proto/mesos.proto
vendored
Normal file
4062
3rdparty/simple-protobuf/test/proto/mesos.proto
vendored
Normal file
File diff suppressed because it is too large
Load Diff
46
3rdparty/simple-protobuf/test/proto/message.proto
vendored
Normal file
46
3rdparty/simple-protobuf/test/proto/message.proto
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
// Specify whether you're using proto2 or proto3 at the top of the file
|
||||
syntax = "proto2";
|
||||
|
||||
/***********/
|
||||
// PACKAGE //
|
||||
/***********/
|
||||
// Your message will be packaged under "tutorial" now
|
||||
// It's good practice to use packages to prevent naming conflicts
|
||||
// between different projects. Fittingly, in C++ our generated
|
||||
// classes will be placed in a namespace of the same name ("tutorial")
|
||||
package tutorial;
|
||||
|
||||
message Person {
|
||||
// REQUIRED V.S. OPTIONAL FIELDS
|
||||
// Be careful about setting fields as required; it can be a headache
|
||||
// if the fields end up becoming optional later on. Some developers
|
||||
// just stick to making all fields optional no matter what.
|
||||
required string name = 1;
|
||||
required int32 id = 2;
|
||||
optional string email = 3;
|
||||
|
||||
// ENUMS
|
||||
// It's good practice to define enums if you have fields with a
|
||||
// fixed set of values they could possibly take. Much more readable
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
|
||||
// NESTED MESSAGE DEFINITIONS
|
||||
// You can define messages within other message definitions
|
||||
message PhoneNumber {
|
||||
message PhoneNumber2 {
|
||||
required string number = 1;
|
||||
optional PhoneType type = 2 [ default = HOME ];
|
||||
}
|
||||
|
||||
required string number = 1;
|
||||
optional PhoneType type = 2 [ default = HOME ];
|
||||
}
|
||||
|
||||
// REPEATED FIELDS
|
||||
// The repeated keyword allows us to have multiple numbers
|
||||
repeated PhoneNumber phones = 4;
|
||||
}
|
||||
22
3rdparty/simple-protobuf/test/proto/name/name.proto.in
vendored
Normal file
22
3rdparty/simple-protobuf/test/proto/name/name.proto.in
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package Test@PB_PACKAGE@;
|
||||
|
||||
//- message with collision values for djb2("name") == djb2("bkfvdzz")
|
||||
message Name {
|
||||
string name = 1;
|
||||
string bkfvdzz = 2;
|
||||
}
|
||||
|
||||
message Recursive {
|
||||
Name value = 1;
|
||||
}
|
||||
|
||||
message Variant {
|
||||
oneof oneof_field {
|
||||
uint32 var_int = 1;
|
||||
string var_string = 2;
|
||||
bytes var_bytes = 3;
|
||||
Name name = 4;
|
||||
}
|
||||
}
|
||||
22
3rdparty/simple-protobuf/test/proto/person/person.proto.in
vendored
Normal file
22
3rdparty/simple-protobuf/test/proto/person/person.proto.in
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package PhoneBook@PB_PACKAGE@;
|
||||
|
||||
message Person {
|
||||
optional string name = 1;
|
||||
optional int32 id = 2; // Unique ID number for this person.
|
||||
optional string email = 3;
|
||||
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
|
||||
message PhoneNumber {
|
||||
required string number = 1; // phone number is always required
|
||||
optional PhoneType type = 2;
|
||||
}
|
||||
// all registered phones
|
||||
repeated PhoneNumber phones = 4;
|
||||
}
|
||||
13
3rdparty/simple-protobuf/test/proto/playlist4_external.proto
vendored
Normal file
13
3rdparty/simple-protobuf/test/proto/playlist4_external.proto
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package spotify.playlist4.proto;
|
||||
|
||||
import "playlist_permission.proto";
|
||||
|
||||
message MetaItem { // its line 18
|
||||
optional ListAttributes attributes = 1;
|
||||
optional int32 length = 2;
|
||||
optional spotify.playlist_permission.proto.Capabilities capabilities = 3;
|
||||
}
|
||||
|
||||
message ListAttributes {}
|
||||
12
3rdparty/simple-protobuf/test/proto/playlist_permission.proto
vendored
Normal file
12
3rdparty/simple-protobuf/test/proto/playlist_permission.proto
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package spotify.playlist_permission.proto;
|
||||
|
||||
message Capabilities { repeated PermissionLevel grantable_level = 1; }
|
||||
|
||||
enum PermissionLevel {
|
||||
UNKNOWN = 0;
|
||||
BLOCKED = 1;
|
||||
VIEWER = 2;
|
||||
CONTRIBUTOR = 3;
|
||||
}
|
||||
199
3rdparty/simple-protobuf/test/proto/proto3.proto
vendored
Normal file
199
3rdparty/simple-protobuf/test/proto/proto3.proto
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package proto3_unittest;
|
||||
|
||||
import "message.proto";
|
||||
|
||||
option optimize_for = SPEED;
|
||||
|
||||
// This proto includes every type of field in both singular and repeated
|
||||
// forms.
|
||||
message TestAllTypes {
|
||||
message NestedMessage {
|
||||
// The field name "b" fails to compile in proto1 because it conflicts
|
||||
// with a local variable named "b" in one of the generated methods. Doh.
|
||||
// This file needs to compile in proto1 to test backwards-compatibility.
|
||||
int32 bb = 1;
|
||||
tutorial.Person person = 2;
|
||||
}
|
||||
|
||||
enum NestedEnum {
|
||||
ZERO = 0;
|
||||
FOO = 1;
|
||||
BAR = 2;
|
||||
BAZ = 3;
|
||||
NEG = -1; // Intentionally negative.
|
||||
}
|
||||
|
||||
// Singular
|
||||
int32 optional_int32 = 1;
|
||||
int64 optional_int64 = 2;
|
||||
uint32 optional_uint32 = 3;
|
||||
uint64 optional_uint64 = 4;
|
||||
sint32 optional_sint32 = 5;
|
||||
sint64 optional_sint64 = 6;
|
||||
fixed32 optional_fixed32 = 7;
|
||||
fixed64 optional_fixed64 = 8;
|
||||
sfixed32 optional_sfixed32 = 9;
|
||||
sfixed64 optional_sfixed64 = 10;
|
||||
float optional_float = 11;
|
||||
double optional_double = 12;
|
||||
bool optional_bool = 13;
|
||||
string optional_string = 14;
|
||||
bytes optional_bytes = 15;
|
||||
|
||||
// Groups are not allowed in proto3.
|
||||
// optional group OptionalGroup = 16 {
|
||||
// optional int32 a = 17;
|
||||
// }
|
||||
|
||||
optional NestedMessage optional_nested_message = 18;
|
||||
ForeignMessage optional_foreign_message = 19;
|
||||
|
||||
NestedEnum optional_nested_enum = 21;
|
||||
ForeignEnum optional_foreign_enum = 22;
|
||||
|
||||
// Omitted (compared to unittest.proto) because proto2 enums are not allowed
|
||||
// inside proto2 messages.
|
||||
//
|
||||
// optional protobuf_unittest_import.ImportEnum optional_import_enum =
|
||||
// 23;
|
||||
|
||||
string optional_string_piece = 24 [ ctype = STRING_PIECE ];
|
||||
string optional_cord = 25 [ ctype = CORD ];
|
||||
|
||||
NestedMessage optional_lazy_message = 27 [ lazy = true ];
|
||||
NestedMessage optional_unverified_lazy_message = 28
|
||||
[ unverified_lazy = true ];
|
||||
|
||||
// Repeated
|
||||
repeated int32 repeated_int32 = 31;
|
||||
repeated int64 repeated_int64 = 32;
|
||||
repeated uint32 repeated_uint32 = 33;
|
||||
repeated uint64 repeated_uint64 = 34;
|
||||
repeated sint32 repeated_sint32 = 35;
|
||||
repeated sint64 repeated_sint64 = 36;
|
||||
repeated fixed32 repeated_fixed32 = 37;
|
||||
repeated fixed64 repeated_fixed64 = 38;
|
||||
repeated sfixed32 repeated_sfixed32 = 39;
|
||||
repeated sfixed64 repeated_sfixed64 = 40;
|
||||
repeated float repeated_float = 41;
|
||||
repeated double repeated_double = 42;
|
||||
repeated bool repeated_bool = 43;
|
||||
repeated string repeated_string = 44;
|
||||
repeated bytes repeated_bytes = 45;
|
||||
|
||||
// Groups are not allowed in proto3.
|
||||
// repeated group RepeatedGroup = 46 {
|
||||
// optional int32 a = 47;
|
||||
// }
|
||||
|
||||
repeated NestedMessage repeated_nested_message = 48;
|
||||
repeated ForeignMessage repeated_foreign_message = 49;
|
||||
|
||||
repeated NestedEnum repeated_nested_enum = 51;
|
||||
repeated ForeignEnum repeated_foreign_enum = 52;
|
||||
|
||||
// Omitted (compared to unittest.proto) because proto2 enums are not allowed
|
||||
// inside proto2 messages.
|
||||
//
|
||||
// repeated protobuf_unittest_import.ImportEnum repeated_import_enum =
|
||||
// 53;
|
||||
|
||||
repeated string repeated_string_piece = 54 [ ctype = STRING_PIECE ];
|
||||
repeated string repeated_cord = 55 [ ctype = CORD ];
|
||||
|
||||
repeated NestedMessage repeated_lazy_message = 57;
|
||||
|
||||
oneof oneof_field {
|
||||
uint32 oneof_uint32 = 111;
|
||||
NestedMessage oneof_nested_message = 112;
|
||||
string oneof_string = 113;
|
||||
bytes oneof_bytes = 114;
|
||||
}
|
||||
}
|
||||
|
||||
// Test messages for packed fields
|
||||
|
||||
message TestPackedTypes {
|
||||
repeated int32 packed_int32 = 90 [ packed = true ];
|
||||
repeated int64 packed_int64 = 91 [ packed = true ];
|
||||
repeated uint32 packed_uint32 = 92 [ packed = true ];
|
||||
repeated uint64 packed_uint64 = 93 [ packed = true ];
|
||||
repeated sint32 packed_sint32 = 94 [ packed = true ];
|
||||
repeated sint64 packed_sint64 = 95 [ packed = true ];
|
||||
repeated fixed32 packed_fixed32 = 96 [ packed = true ];
|
||||
repeated fixed64 packed_fixed64 = 97 [ packed = true ];
|
||||
repeated sfixed32 packed_sfixed32 = 98 [ packed = true ];
|
||||
repeated sfixed64 packed_sfixed64 = 99 [ packed = true ];
|
||||
repeated float packed_float = 100 [ packed = true ];
|
||||
repeated double packed_double = 101 [ packed = true ];
|
||||
repeated bool packed_bool = 102 [ packed = true ];
|
||||
repeated ForeignEnum packed_enum = 103 [ packed = true ];
|
||||
}
|
||||
|
||||
// Explicitly set packed to false
|
||||
message TestUnpackedTypes {
|
||||
repeated int32 repeated_int32 = 1 [ packed = false ];
|
||||
repeated int64 repeated_int64 = 2 [ packed = false ];
|
||||
repeated uint32 repeated_uint32 = 3 [ packed = false ];
|
||||
repeated uint64 repeated_uint64 = 4 [ packed = false ];
|
||||
repeated sint32 repeated_sint32 = 5 [ packed = false ];
|
||||
repeated sint64 repeated_sint64 = 6 [ packed = false ];
|
||||
repeated fixed32 repeated_fixed32 = 7 [ packed = false ];
|
||||
repeated fixed64 repeated_fixed64 = 8 [ packed = false ];
|
||||
repeated sfixed32 repeated_sfixed32 = 9 [ packed = false ];
|
||||
repeated sfixed64 repeated_sfixed64 = 10 [ packed = false ];
|
||||
repeated float repeated_float = 11 [ packed = false ];
|
||||
repeated double repeated_double = 12 [ packed = false ];
|
||||
repeated bool repeated_bool = 13 [ packed = false ];
|
||||
repeated TestAllTypes.NestedEnum repeated_nested_enum = 14
|
||||
[ packed = false ];
|
||||
}
|
||||
|
||||
// This proto includes a recursively nested message.
|
||||
message NestedTestAllTypes {
|
||||
NestedTestAllTypes child = 1;
|
||||
TestAllTypes payload = 2;
|
||||
}
|
||||
|
||||
// Define these after TestAllTypes to make sure the compiler can handle
|
||||
// that.
|
||||
message ForeignMessage { int32 c = 1; }
|
||||
|
||||
enum ForeignEnum {
|
||||
FOREIGN_ZERO = 0;
|
||||
FOREIGN_FOO = 4;
|
||||
FOREIGN_BAR = 5;
|
||||
FOREIGN_BAZ = 6;
|
||||
}
|
||||
|
||||
// TestEmptyMessage is used to test behavior of unknown fields.
|
||||
message TestEmptyMessage {}
|
||||
|
||||
// TestMessageWithDummy is also used to test behavior of unknown fields.
|
||||
message TestMessageWithDummy {
|
||||
// This field is only here for triggering copy-on-write; it's not intended
|
||||
// to be serialized.
|
||||
bool dummy = 536870911;
|
||||
}
|
||||
|
||||
// Same layout as TestOneof2 in unittest.proto to test unknown enum value
|
||||
// parsing behavior in oneof.
|
||||
message TestOneof2 {
|
||||
oneof foo { NestedEnum foo_enum = 6; }
|
||||
|
||||
enum NestedEnum {
|
||||
UNKNOWN = 0;
|
||||
FOO = 1;
|
||||
BAR = 2;
|
||||
BAZ = 3;
|
||||
}
|
||||
}
|
||||
1243
3rdparty/simple-protobuf/test/proto/scalar/scalar.proto.in
vendored
Normal file
1243
3rdparty/simple-protobuf/test/proto/scalar/scalar.proto.in
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user