[struct_pb]Refact struct pb (#680)

This commit is contained in:
qicosmos 2024-05-29 11:43:20 +08:00 committed by GitHub
parent 350fbc4352
commit 476b5842d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
117 changed files with 4222 additions and 14841 deletions

View File

@ -30,7 +30,7 @@ jobs:
CXX=g++ CC=gcc CXX=g++ CC=gcc
cmake -B ${{github.workspace}}/build \ cmake -B ${{github.workspace}}/build \
-DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_BUILD_TYPE=Debug \
-DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_STRUCT_PB=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARK=OFF -DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARK=OFF
cmake --build ${{github.workspace}}/build -j cmake --build ${{github.workspace}}/build -j
cd ${{github.workspace}}/build/output/tests cd ${{github.workspace}}/build/output/tests
./struct_pack_test ./struct_pack_test

View File

@ -98,7 +98,7 @@ jobs:
cmake -B ${{github.workspace}}/build -G Ninja \ cmake -B ${{github.workspace}}/build -G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.mode}} -DBUILD_WITH_LIBCXX=${{matrix.libcxx}} -DYLT_ENABLE_SSL=${{matrix.ssl}} \ -DCMAKE_BUILD_TYPE=${{matrix.mode}} -DBUILD_WITH_LIBCXX=${{matrix.libcxx}} -DYLT_ENABLE_SSL=${{matrix.ssl}} \
-DUSE_CCACHE=${{env.ccache}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17\ -DUSE_CCACHE=${{env.ccache}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17\
-DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_STRUCT_PB=ON -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}} run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}}
@ -146,7 +146,7 @@ jobs:
-DBUILD_WITH_LIBCXX=${{matrix.libcxx}} \ -DBUILD_WITH_LIBCXX=${{matrix.libcxx}} \
-DYLT_ENABLE_IO_URING=${{matrix.io_uring}} \ -DYLT_ENABLE_IO_URING=${{matrix.io_uring}} \
-DUSE_CCACHE=${{env.ccache}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17\ -DUSE_CCACHE=${{env.ccache}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17\
-DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_PACK=OFF -DBUILD_STRUCT_PB=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_PACK=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}} run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}}

View File

@ -80,7 +80,7 @@ jobs:
cmake -B ${{github.workspace}}/build -G Ninja \ cmake -B ${{github.workspace}}/build -G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.mode}} -DBUILD_WITH_LIBCXX=${{matrix.libcxx}} -DYLT_ENABLE_SSL=${{matrix.ssl}} \ -DCMAKE_BUILD_TYPE=${{matrix.mode}} -DBUILD_WITH_LIBCXX=${{matrix.libcxx}} -DYLT_ENABLE_SSL=${{matrix.ssl}} \
-DUSE_CCACHE=${{env.ccache}} \ -DUSE_CCACHE=${{env.ccache}} \
-DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_STRUCT_PB=ON -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}} run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}}
@ -118,7 +118,7 @@ jobs:
-DCMAKE_BUILD_TYPE=${{matrix.mode}} \ -DCMAKE_BUILD_TYPE=${{matrix.mode}} \
-DYLT_ENABLE_IO_URING=${{matrix.io_uring}} \ -DYLT_ENABLE_IO_URING=${{matrix.io_uring}} \
-DUSE_CCACHE=${{env.ccache}} \ -DUSE_CCACHE=${{env.ccache}} \
-DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_PACK=OFF -DBUILD_STRUCT_PB=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_PACK=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}} run: cmake --build ${{github.workspace}}/build --config ${{matrix.mode}}

View File

@ -72,7 +72,7 @@ jobs:
with: with:
key: ${{ github.job }}-${{ matrix.mode}}-arch-${{ matrix.arch}} key: ${{ github.job }}-${{ matrix.mode}}-arch-${{ matrix.arch}}
- name: Configure CMake - name: Configure CMake
run: cmake -B ${{github.workspace}}\build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.mode }} -DYLT_ENABLE_SSL=${{matrix.ssl}} -DUSE_CCACHE=ON -DENABLE_CPP_20=OFF run: cmake -B ${{github.workspace}}\build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.mode }} -DYLT_ENABLE_SSL=${{matrix.ssl}} -DBUILD_STRUCT_PB=OFF -DUSE_CCACHE=ON -DENABLE_CPP_20=OFF
- name: Build - name: Build
run: cmake --build ${{github.workspace}}\build run: cmake --build ${{github.workspace}}\build
- name: Test - name: Test

View File

@ -22,7 +22,6 @@ if(CMAKE_PROJECT_NAME STREQUAL "yaLanTingLibs") # if ylt is top-level project
include_directories(src/include) include_directories(src/include)
include(cmake/utils.cmake) include(cmake/utils.cmake)
include(cmake/struct_pb.cmake)
include(cmake/build.cmake) include(cmake/build.cmake)
include(cmake/develop.cmake) include(cmake/develop.cmake)
# add project config, such as enable_ssl. # add project config, such as enable_ssl.

View File

@ -504,11 +504,9 @@ No dependency.
No dependency. No dependency.
### struct_pb (optional) ### struct_pb
In default, struct_pb wont be installed. You need install protobuf manually. No dependency.
- [protobuf](https://protobuf.dev/)
## Standalone sublibraries ## Standalone sublibraries

View File

@ -1,224 +0,0 @@
function(protobuf_generate_modified)
find_package(Protobuf REQUIRED)
set(_options APPEND_PATH DESCRIPTORS)
set(_singleargs LANGUAGE OUT_VAR EXPORT_MACRO PROTOC_OUT_DIR PLUGIN PROTOC_OPTION)
if(COMMAND target_sources)
list(APPEND _singleargs TARGET)
endif()
set(_multiargs PROTOS IMPORT_DIRS GENERATE_EXTENSIONS)
cmake_parse_arguments(protobuf_generate "${_options}" "${_singleargs}" "${_multiargs}" "${ARGN}")
if(NOT protobuf_generate_PROTOS AND NOT protobuf_generate_TARGET)
message(SEND_ERROR "Error: protobuf_generate called without any targets or source files")
return()
endif()
if(NOT protobuf_generate_OUT_VAR AND NOT protobuf_generate_TARGET)
message(SEND_ERROR "Error: protobuf_generate called without a target or output variable")
return()
endif()
if(NOT protobuf_generate_LANGUAGE)
set(protobuf_generate_LANGUAGE struct_pb)
endif()
string(TOLOWER ${protobuf_generate_LANGUAGE} protobuf_generate_LANGUAGE)
if(NOT protobuf_generate_PROTOC_OUT_DIR)
set(protobuf_generate_PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
endif()
set(_opt)
if(protobuf_generate_PROTOC_OPTION)
set(_opt "${protobuf_generate_PROTOC_OPTION}")
endif()
if(protobuf_generate_EXPORT_MACRO AND protobuf_generate_LANGUAGE STREQUAL cpp)
set(_opt "${opt},dllexport_decl=${protobuf_generate_EXPORT_MACRO}")
endif()
set(_opt "${_opt}:")
if(protobuf_generate_PLUGIN)
set(_plugin "--plugin=${protobuf_generate_PLUGIN}")
endif()
if(NOT protobuf_generate_GENERATE_EXTENSIONS)
if(protobuf_generate_LANGUAGE STREQUAL cpp)
set(protobuf_generate_GENERATE_EXTENSIONS .pb.h .pb.cc)
elseif(protobuf_generate_LANGUAGE STREQUAL python)
set(protobuf_generate_GENERATE_EXTENSIONS _pb2.py)
elseif(protobuf_generate_LANGUAGE STREQUAL struct_pb)
set(protobuf_generate_GENERATE_EXTENSIONS .struct_pb.h .struct_pb.cc)
else()
message(SEND_ERROR "Error: protobuf_generate given unknown Language ${LANGUAGE}, please provide a value for GENERATE_EXTENSIONS")
return()
endif()
endif()
if(protobuf_generate_TARGET)
get_target_property(_source_list ${protobuf_generate_TARGET} SOURCES)
foreach(_file ${_source_list})
if(_file MATCHES "proto$")
list(APPEND protobuf_generate_PROTOS ${_file})
endif()
endforeach()
endif()
if(NOT protobuf_generate_PROTOS)
message(SEND_ERROR "Error: protobuf_generate could not find any .proto files")
return()
endif()
if(protobuf_generate_APPEND_PATH)
# Create an include path for each file specified
foreach(_file ${protobuf_generate_PROTOS})
get_filename_component(_abs_file ${_file} ABSOLUTE)
get_filename_component(_abs_path ${_abs_file} PATH)
list(FIND _protobuf_include_path ${_abs_path} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${_abs_path})
endif()
endforeach()
else()
set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
endif()
foreach(DIR ${protobuf_generate_IMPORT_DIRS})
get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1)
list(APPEND _protobuf_include_path -I ${ABS_PATH})
endif()
endforeach()
set(_generated_srcs_all)
foreach(_proto ${protobuf_generate_PROTOS})
get_filename_component(_abs_file ${_proto} ABSOLUTE)
get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
get_filename_component(_basename ${_proto} NAME_WLE)
file(RELATIVE_PATH _rel_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_abs_dir})
set(_possible_rel_dir)
if (NOT protobuf_generate_APPEND_PATH)
set(_possible_rel_dir ${_rel_dir}/)
endif()
set(_generated_srcs)
foreach(_ext ${protobuf_generate_GENERATE_EXTENSIONS})
list(APPEND _generated_srcs "${protobuf_generate_PROTOC_OUT_DIR}/${_possible_rel_dir}${_basename}${_ext}")
endforeach()
if(protobuf_generate_DESCRIPTORS AND protobuf_generate_LANGUAGE STREQUAL cpp)
set(_descriptor_file "${CMAKE_CURRENT_BINARY_DIR}/${_basename}.desc")
set(_dll_desc_out "--descriptor_set_out=${_descriptor_file}")
list(APPEND _generated_srcs ${_descriptor_file})
endif()
list(APPEND _generated_srcs_all ${_generated_srcs})
add_custom_command(
OUTPUT ${_generated_srcs}
COMMAND protobuf::protoc
ARGS --${protobuf_generate_LANGUAGE}_out ${_opt}${protobuf_generate_PROTOC_OUT_DIR} ${_plugin} ${_dll_desc_out} ${_protobuf_include_path} ${_abs_file}
DEPENDS ${_abs_file} protobuf::protoc
COMMENT "Running ${protobuf_generate_LANGUAGE} protocol buffer compiler on ${_proto}"
VERBATIM )
endforeach()
set_source_files_properties(${_generated_srcs_all} PROPERTIES GENERATED TRUE)
if(protobuf_generate_OUT_VAR)
set(${protobuf_generate_OUT_VAR} ${_generated_srcs_all} PARENT_SCOPE)
endif()
if(protobuf_generate_TARGET)
target_sources(${protobuf_generate_TARGET} PRIVATE ${_generated_srcs_all})
target_include_directories(${protobuf_generate_TARGET} PUBLIC ${protobuf_generate_PROTOC_OUT_DIR})
endif()
endfunction()
function(protobuf_generate_struct_pb SRCS HDRS)
cmake_parse_arguments(protobuf_generate_struct_pb "" "EXPORT_MACRO;DESCRIPTORS;OPTION" "" ${ARGN})
set(_proto_files "${protobuf_generate_struct_pb_UNPARSED_ARGUMENTS}")
if(NOT _proto_files)
message(SEND_ERROR "Error: PROTOBUF_GENERATE_STRUCT_PB() called without any proto files")
return()
endif()
if(PROTOBUF_GENERATE_STRUCT_PB_APPEND_PATH)
set(_append_arg APPEND_PATH)
endif()
if(protobuf_generate_struct_pb_DESCRIPTORS)
set(_descriptors DESCRIPTORS)
endif()
if(protobuf_generate_struct_pb_OPTION)
set(_opt ${protobuf_generate_struct_pb_OPTION})
endif()
if(DEFINED PROTOBUF_IMPORT_DIRS AND NOT DEFINED Protobuf_IMPORT_DIRS)
set(Protobuf_IMPORT_DIRS "${PROTOBUF_IMPORT_DIRS}")
endif()
if(DEFINED Protobuf_IMPORT_DIRS)
set(_import_arg IMPORT_DIRS ${Protobuf_IMPORT_DIRS})
endif()
set(_outvar)
protobuf_generate_modified(${_append_arg} ${_descriptors}
LANGUAGE struct_pb EXPORT_MACRO ${protobuf_generate_struct_pb_EXPORT_MACRO}
PLUGIN $<TARGET_FILE:protoc-gen-struct_pb>
OUT_VAR _outvar ${_import_arg} PROTOS ${_proto_files}
PROTOC_OPTION ${_opt}
)
set(${SRCS})
set(${HDRS})
if(protobuf_generate_struct_pb_DESCRIPTORS)
set(${protobuf_generate_struct_pb_DESCRIPTORS})
endif()
foreach(_file ${_outvar})
if(_file MATCHES "cc$")
list(APPEND ${SRCS} ${_file})
elseif(_file MATCHES "desc$")
list(APPEND ${protobuf_generate_struct_pb_DESCRIPTORS} ${_file})
else()
list(APPEND ${HDRS} ${_file})
endif()
endforeach()
set(${SRCS} ${${SRCS}} PARENT_SCOPE)
set(${HDRS} ${${HDRS}} PARENT_SCOPE)
if(protobuf_generate_struct_pb_DESCRIPTORS)
set(${protobuf_generate_struct_pb_DESCRIPTORS} "${${protobuf_generate_struct_pb_DESCRIPTORS}}" PARENT_SCOPE)
endif()
endfunction()
function(target_protos_struct_pb target)
set(_options APPEND_PATH DESCRIPTORS)
set(_singleargs LANGUAGE EXPORT_MACRO PROTOC_OUT_DIR PLUGIN OPTION)
set(_multiargs IMPORT_DIRS PRIVATE PUBLIC)
cmake_parse_arguments(target_protos_struct_pb "${_options}" "${_singleargs}" "${_multiargs}" "${ARGN}")
set(_proto_files "${target_protos_struct_pb_PRIVATE}" "${target_protos_struct_pb_PUBLIC}")
if (NOT _proto_files)
message(SEND_ERROR "Error: TARGET_PROTOS_STRUCT_PB() called without any proto files")
return()
endif ()
if (DEFINED PROTOBUF_IMPORT_DIRS AND NOT DEFINED Protobuf_IMPORT_DIRS)
set(Protobuf_IMPORT_DIRS "${PROTOBUF_IMPORT_DIRS}")
endif ()
if (DEFINED Protobuf_IMPORT_DIRS)
set(_import_arg IMPORT_DIRS ${Protobuf_IMPORT_DIRS})
endif ()
if (target_protos_struct_pb_OPTION)
set(_opt ${target_protos_struct_pb_OPTION})
endif ()
protobuf_generate_modified(
TARGET ${target}
LANGUAGE struct_pb EXPORT_MACRO ${target_protos_struct_pb_EXPORT_MACRO}
PLUGIN $<TARGET_FILE:protoc-gen-struct_pb>
${_import_arg} PROTOS ${_proto_files}
PROTOC_OPTION ${_opt}
)
endfunction()

View File

@ -22,13 +22,13 @@ endif()
foreach(child ${children}) foreach(child ${children})
get_filename_component(subdir_name ${child} NAME) get_filename_component(subdir_name ${child} NAME)
string(TOUPPER ${subdir_name} subdir_name) string(TOUPPER ${subdir_name} subdir_name)
message(STATUS "BUILD_${subdir_name}: ${BUILD_${subdir_name}}") if((${subdir_name} STREQUAL "STRUCT_PACK" OR ${subdir_name} STREQUAL "STRUCT_PB") AND (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC"))
endforeach() message(STATUS "skip ${subdir_name}")
continue()
endif()
foreach(child ${children})
get_filename_component(subdir_name ${child} NAME)
string(TOUPPER ${subdir_name} subdir_name)
if (BUILD_${subdir_name}) if (BUILD_${subdir_name})
message(STATUS "BUILD_${subdir_name}: ${BUILD_${subdir_name}}")
if(BUILD_EXAMPLES AND EXISTS ${child}/examples) if(BUILD_EXAMPLES AND EXISTS ${child}/examples)
add_subdirectory(${child}/examples) add_subdirectory(${child}/examples)
endif() endif()
@ -40,7 +40,4 @@ foreach(child ${children})
endif() endif()
endif() endif()
endforeach() endforeach()
if (BUILD_STRUCT_PB)
add_subdirectory(src/struct_pb)
endif()
message(STATUS "--------------------------------------------") message(STATUS "--------------------------------------------")

View File

@ -0,0 +1,135 @@
#pragma once
namespace iguana {
struct sint32_t {
using value_type = int32_t;
int32_t val;
bool operator==(const sint32_t& other) const { return val == other.val; }
};
inline bool operator==(sint32_t value1, int32_t value2) {
return value1.val == value2;
}
// for key in std::map
inline bool operator<(const sint32_t& lhs, const sint32_t& rhs) {
return lhs.val < rhs.val;
}
struct sint64_t {
using value_type = int64_t;
int64_t val;
bool operator==(const sint64_t& other) const { return val == other.val; }
};
inline bool operator==(sint64_t value1, int64_t value2) {
return value1.val == value2;
}
inline bool operator<(const sint64_t& lhs, const sint64_t& rhs) {
return lhs.val < rhs.val;
}
struct fixed32_t {
using value_type = uint32_t;
uint32_t val;
bool operator==(const fixed32_t& other) const { return val == other.val; }
};
inline bool operator==(fixed32_t value1, uint32_t value2) {
return value1.val == value2;
}
inline bool operator<(const fixed32_t& lhs, const fixed32_t& rhs) {
return lhs.val < rhs.val;
}
struct fixed64_t {
using value_type = uint64_t;
uint64_t val;
bool operator==(const fixed64_t& other) const { return val == other.val; }
};
inline bool operator==(fixed64_t value1, uint64_t value2) {
return value1.val == value2;
}
inline bool operator<(const fixed64_t& lhs, const fixed64_t& rhs) {
return lhs.val < rhs.val;
}
struct sfixed32_t {
using value_type = int32_t;
int32_t val;
bool operator==(const sfixed32_t& other) const { return val == other.val; }
};
inline bool operator==(sfixed32_t value1, int32_t value2) {
return value1.val == value2;
}
inline bool operator<(const sfixed32_t& lhs, const sfixed32_t& rhs) {
return lhs.val < rhs.val;
}
struct sfixed64_t {
using value_type = int64_t;
int64_t val;
bool operator==(const sfixed64_t& other) const { return val == other.val; }
};
inline bool operator==(sfixed64_t value1, int64_t value2) {
return value1.val == value2;
}
inline bool operator<(const sfixed64_t& lhs, const sfixed64_t& rhs) {
return lhs.val < rhs.val;
}
} // namespace iguana
// for key in std::unordered_map
namespace std {
template <>
struct hash<iguana::sint32_t> {
size_t operator()(const iguana::sint32_t& x) const noexcept {
return std::hash<int32_t>()(x.val);
}
};
template <>
struct hash<iguana::sint64_t> {
size_t operator()(const iguana::sint64_t& x) const noexcept {
return std::hash<int64_t>()(x.val);
}
};
template <>
struct hash<iguana::fixed32_t> {
size_t operator()(const iguana::fixed32_t& x) const noexcept {
return std::hash<uint32_t>()(x.val);
}
};
template <>
struct hash<iguana::fixed64_t> {
size_t operator()(const iguana::fixed64_t& x) const noexcept {
return std::hash<uint64_t>()(x.val);
}
};
template <>
struct hash<iguana::sfixed32_t> {
size_t operator()(const iguana::sfixed32_t& x) const noexcept {
return std::hash<int32_t>()(x.val);
}
};
template <>
struct hash<iguana::sfixed64_t> {
size_t operator()(const iguana::sfixed64_t& x) const noexcept {
return std::hash<int64_t>()(x.val);
}
};
} // namespace std

View File

@ -0,0 +1,131 @@
#pragma once
#include <cstddef>
#include <string>
#include <utility>
#include <vector>
namespace iguana::detail {
#if __cpp_lib_string_resize_and_overwrite >= 202110L
template <typename ch>
inline void resize(std::basic_string<ch> &str, std::size_t sz) {
str.resize_and_overwrite(sz, [sz](ch *, std::size_t) {
return sz;
});
}
#elif (defined(_MSC_VER) && _MSC_VER <= 1920)
// old msvc don't support visit private, discard it.
#else
template <typename Function, Function func_ptr>
class string_thief {
public:
friend void string_set_length_hacker(std::string &self, std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mysize = sz;
#else
#if defined(_LIBCPP_VERSION)
(self.*func_ptr)(sz);
#else
#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__)
(self.*func_ptr)()->_M_set_length_and_sharable(sz);
#else
#if defined(__GLIBCXX__)
(self.*func_ptr)(sz);
#endif
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
#if (_GLIBCXX_USE_CXX11_ABI == 0)
template class string_thief<decltype(&std::string::_M_rep),
&std::string::_M_rep>;
#else
template class string_thief<decltype(&std::string::_M_set_length),
&std::string::_M_set_length>;
#endif
#elif defined(_LIBCPP_VERSION)
template class string_thief<decltype(&std::string::__set_size),
&std::string::__set_size>;
#elif defined(_MSVC_STL_VERSION)
template class string_thief<decltype(&std::string::_Mypair),
&std::string::_Mypair>;
#endif
void string_set_length_hacker(std::string &, std::size_t);
template <typename ch>
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
defined(_MSVC_STL_VERSION)
if (sz > str.capacity()) {
str.reserve(sz);
}
string_set_length_hacker(str, sz);
str[sz] = '\0';
#else
raw_str.resize(sz);
#endif
}
#endif
#if (defined(_MSC_VER) && _MSC_VER <= 1920)
#else
void vector_set_length_hacker(std::vector<char> &self, std::size_t sz);
template <typename Function, Function func_ptr>
class vector_thief {
public:
friend void vector_set_length_hacker(std::vector<char> &self,
std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mylast = self.data() + sz;
#else
#if defined(_LIBCPP_VERSION)
#if _LIBCPP_VERSION < 14000
((*(std::__vector_base<char, std::allocator<char> > *)(&self)).*func_ptr) =
self.data() + sz;
#else
(self.*func_ptr) = self.data() + sz;
#endif
#else
#if defined(__GLIBCXX__)
((*(std::_Vector_base<char, std::allocator<char> > *)(&self)).*func_ptr)
._M_finish = self.data() + sz;
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
template class vector_thief<decltype(&std::vector<char>::_M_impl),
&std::vector<char>::_M_impl>;
#elif defined(_LIBCPP_VERSION)
template class vector_thief<decltype(&std::vector<char>::__end_),
&std::vector<char>::__end_>;
#elif defined(_MSVC_STL_VERSION)
template class vector_thief<decltype(&std::vector<char>::_Mypair),
&std::vector<char>::_Mypair>;
#endif
template <typename ch>
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
#if defined(__GLIBCXX__) || \
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
defined(_MSVC_STL_VERSION)
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
vec.reserve(sz);
vector_set_length_hacker(vec, sz);
#else
raw_vec.resize(sz);
#endif
}
#endif
}; // namespace iguana::detail

View File

@ -79,5 +79,39 @@ struct member_tratis<T Owner::*> {
template <typename T> template <typename T>
inline constexpr bool is_int64_v = inline constexpr bool is_int64_v =
std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>; std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>;
template <typename T>
struct is_variant : std::false_type {};
template <typename... T>
struct is_variant<std::variant<T...>> : std::true_type {};
template <class T>
struct member_traits {
using value_type = T;
};
template <class T, class Owner>
struct member_traits<T Owner::*> {
using owner_type = Owner;
using value_type = T;
};
template <class T>
using member_value_type_t = typename member_traits<T>::value_type;
template <typename T, std::size_t I, typename = void>
struct variant_type_at {
using type = T;
};
template <typename T, std::size_t I>
struct variant_type_at<T, I, std::enable_if_t<is_variant<T>::value>> {
using type = std::variant_alternative_t<I, T>;
};
template <typename T, std::size_t I>
using variant_type_at_t =
typename variant_type_at<typename member_traits<T>::value_type, I>::type;
} // namespace iguana } // namespace iguana
#endif // SERIALIZE_TRAITS_HPP #endif // SERIALIZE_TRAITS_HPP

View File

@ -0,0 +1,257 @@
#pragma once
#include "detail/string_resize.hpp"
#include "pb_util.hpp"
namespace iguana {
namespace detail {
template <typename T>
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
uint32_t field_no = 0);
template <typename T>
IGUANA_INLINE void decode_pair_value(T& val, std::string_view& pb_str) {
size_t pos;
uint32_t key = detail::decode_varint(pb_str, pos);
pb_str = pb_str.substr(pos);
WireType wire_type = static_cast<WireType>(key & 0b0111);
if (wire_type != detail::get_wire_type<std::remove_reference_t<T>>()) {
return;
}
from_pb_impl(val, pb_str);
}
template <typename T>
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
uint32_t field_no) {
size_t pos = 0;
if constexpr (is_reflection_v<T>) {
size_t pos;
uint32_t size = detail::decode_varint(pb_str, pos);
pb_str = pb_str.substr(pos);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
}
if (size == 0) {
return;
}
from_pb(val, pb_str.substr(0, size));
pb_str = pb_str.substr(size);
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// item_type non-packed
while (!pb_str.empty()) {
item_type item{};
from_pb_impl(item, pb_str);
val.push_back(std::move(item));
if (pb_str.empty()) {
break;
}
uint32_t key = detail::decode_varint(pb_str, pos);
uint32_t field_number = key >> 3;
if (field_number != field_no) {
break;
}
else {
pb_str = pb_str.substr(pos);
}
}
}
else {
// item_type packed
size_t pos;
uint32_t size = detail::decode_varint(pb_str, pos);
pb_str = pb_str.substr(pos);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument(
"Invalid fixed int value: too few bytes.");
}
using item_type = typename T::value_type;
size_t start = pb_str.size();
while (!pb_str.empty()) {
item_type item;
from_pb_impl(item, pb_str);
val.push_back(std::move(item));
if (start - pb_str.size() == size) {
break;
}
}
}
}
else if constexpr (is_map_container<T>::value) {
using item_type = std::pair<typename T::key_type, typename T::mapped_type>;
while (!pb_str.empty()) {
size_t pos;
uint32_t size = detail::decode_varint(pb_str, pos);
pb_str = pb_str.substr(pos);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument(
"Invalid fixed int value: too few bytes.");
}
item_type item = {};
decode_pair_value(item.first, pb_str);
decode_pair_value(item.second, pb_str);
val.emplace(std::move(item));
if (pb_str.empty()) {
break;
}
uint32_t key = detail::decode_varint(pb_str, pos);
uint32_t field_number = key >> 3;
if (field_number != field_no) {
break;
}
pb_str = pb_str.substr(pos);
}
}
else if constexpr (std::is_integral_v<T>) {
val = static_cast<T>(detail::decode_varint(pb_str, pos));
pb_str = pb_str.substr(pos);
}
else if constexpr (detail::is_signed_varint_v<T>) {
constexpr size_t len = sizeof(typename T::value_type);
uint64_t temp = detail::decode_varint(pb_str, pos);
if constexpr (len == 8) {
val.val = detail::decode_zigzag(temp);
}
else {
val.val = detail::decode_zigzag(static_cast<uint32_t>(temp));
}
pb_str = pb_str.substr(pos);
}
else if constexpr (detail::is_fixed_v<T>) {
constexpr size_t size = sizeof(typename T::value_type);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
}
memcpy(&(val.val), pb_str.data(), size);
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
constexpr size_t size = sizeof(T);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
}
memcpy(&(val), pb_str.data(), size);
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
size_t size = detail::decode_varint(pb_str, pos);
if (pb_str.size() < pos + size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid string value: too few bytes.");
}
if constexpr (std::is_same_v<T, std::string_view>) {
val = std::string_view(pb_str.data() + pos, size);
}
else {
detail::resize(val, size);
memcpy(val.data(), pb_str.data() + pos, size);
}
pb_str = pb_str.substr(size + pos);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
U value{};
from_pb_impl(value, pb_str);
val = static_cast<T>(value);
}
else if constexpr (optional_v<T>) {
from_pb_impl(val.emplace(), pb_str);
}
else {
static_assert(!sizeof(T), "err");
}
}
template <typename T, typename Field>
IGUANA_INLINE void parse_oneof(T& t, const Field& f, std::string_view& pb_str) {
using item_type = typename std::decay_t<Field>::sub_type;
from_pb_impl(t.template emplace<item_type>(), pb_str, f.field_no);
}
} // namespace detail
template <typename T>
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) {
if (pb_str.empty())
IGUANA_UNLIKELY { return; }
size_t pos = 0;
uint32_t key = detail::decode_varint(pb_str, pos);
WireType wire_type = static_cast<WireType>(key & 0b0111);
uint32_t field_number = key >> 3;
#ifdef SEQUENTIAL_PARSE
constexpr static auto tp = get_members_tuple<T>();
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tp)>>;
bool parse_done = false;
detail::for_each_n(
[&](auto i) IGUANA__INLINE_LAMBDA {
constexpr auto val = std::get<decltype(i)::value>(tp);
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
using value_type = typename std::decay_t<decltype(val)>::value_type;
// sub_type is the element type when value_type is the variant type;
// otherwise, they are the same.
if (parse_done || field_number != val.field_no) {
return;
}
pb_str = pb_str.substr(pos);
if (wire_type != detail::get_wire_type<sub_type>())
IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); }
if constexpr (variant_v<value_type>) {
detail::parse_oneof(val.value(t), val, pb_str);
}
else {
detail::from_pb_impl(val.value(t), pb_str, val.field_no);
}
if (pb_str.empty()) {
parse_done = true;
return;
}
key = detail::decode_varint(pb_str, pos);
wire_type = static_cast<WireType>(key & 0b0111);
field_number = key >> 3;
},
std::make_index_sequence<SIZE>{});
if (parse_done)
IGUANA_LIKELY { return; }
#endif
while (true) {
pb_str = pb_str.substr(pos);
constexpr static auto map = get_members<T>();
auto& member = map.at(field_number);
std::visit(
[&t, &pb_str, wire_type](auto& val) {
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
using value_type = typename std::decay_t<decltype(val)>::value_type;
if (wire_type != detail::get_wire_type<sub_type>()) {
throw std::runtime_error("unmatched wire_type");
}
if constexpr (variant_v<value_type>) {
detail::parse_oneof(val.value(t), val, pb_str);
}
else {
detail::from_pb_impl(val.value(t), pb_str, val.field_no);
}
},
member);
if (!pb_str.empty())
IGUANA_LIKELY {
key = detail::decode_varint(pb_str, pos);
wire_type = static_cast<WireType>(key & 0b0111);
field_number = key >> 3;
}
else {
return;
}
}
}
} // namespace iguana

View File

@ -0,0 +1,660 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstring>
#include <map>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "detail/pb_type.hpp"
#include "reflection.hpp"
#include "util.hpp"
namespace iguana {
enum class WireType : uint32_t {
Varint = 0,
Fixed64 = 1,
LengthDelimeted = 2,
StartGroup = 3,
EndGroup = 4,
Fixed32 = 5,
Unknown
};
template <typename T, typename Stream>
IGUANA_INLINE void to_pb(T& t, Stream& out);
template <typename T>
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str);
using base = detail::base;
template <typename T, typename U>
IGUANA_INLINE constexpr size_t member_offset(T* t, U T::*member) {
return (char*)&(t->*member) - (char*)t;
}
template <typename T>
struct base_impl : public base {
void to_pb(std::string& str) override {
iguana::to_pb(*(static_cast<T*>(this)), str);
}
void from_pb(std::string_view str) override {
iguana::from_pb(*(static_cast<T*>(this)), str);
}
iguana::detail::field_info get_field_info(std::string_view name) override {
static constexpr auto map = iguana::get_members<T>();
iguana::detail::field_info info{};
for (auto [no, field] : map) {
if (info.offset > 0) {
break;
}
std::visit(
[&](auto val) {
if (val.field_name == name) {
info.offset = member_offset((T*)this, val.member_ptr);
using value_type = typename decltype(val)::value_type;
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
info.type_name = type_string<value_type>();
#endif
}
},
field);
}
return info;
}
std::vector<std::string_view> get_fields_name() override {
static constexpr auto map = iguana::get_members<T>();
std::vector<std::string_view> vec;
for (auto [no, val] : map) {
std::visit(
[&](auto& field) {
vec.push_back(std::string_view(field.field_name.data(),
field.field_name.size()));
},
val);
}
return vec;
}
virtual ~base_impl() {}
size_t cache_size = 0;
};
template <typename T>
constexpr bool inherits_from_base_v = std::is_base_of_v<base, T>;
IGUANA_INLINE std::shared_ptr<base> create_instance(std::string_view name) {
auto it = iguana::detail::g_pb_map.find(name);
if (it == iguana::detail::g_pb_map.end()) {
throw std::invalid_argument(std::string(name) +
"not inheried from iguana::base_impl");
}
return it->second();
}
namespace detail {
template <typename T>
constexpr bool is_fixed_v =
std::is_same_v<T, fixed32_t> || std::is_same_v<T, fixed64_t> ||
std::is_same_v<T, sfixed32_t> || std::is_same_v<T, sfixed64_t>;
template <typename T>
constexpr bool is_signed_varint_v =
std::is_same_v<T, sint32_t> || std::is_same_v<T, sint64_t>;
template <typename T>
constexpr inline WireType get_wire_type() {
if constexpr (std::is_integral_v<T> || is_signed_varint_v<T> ||
std::is_enum_v<T> || std::is_same_v<T, bool>) {
return WireType::Varint;
}
else if constexpr (std::is_same_v<T, fixed32_t> ||
std::is_same_v<T, sfixed32_t> ||
std::is_same_v<T, float>) {
return WireType::Fixed32;
}
else if constexpr (std::is_same_v<T, fixed64_t> ||
std::is_same_v<T, sfixed64_t> ||
std::is_same_v<T, double>) {
return WireType::Fixed64;
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
is_reflection_v<T> || is_sequence_container<T>::value ||
is_map_container<T>::value) {
return WireType::LengthDelimeted;
}
else if constexpr (optional_v<T>) {
return get_wire_type<typename T::value_type>();
}
else {
throw std::runtime_error("unknown type");
}
}
template <typename T>
constexpr bool is_lenprefix_v = (get_wire_type<T>() ==
WireType::LengthDelimeted);
[[nodiscard]] IGUANA_INLINE uint32_t encode_zigzag(int32_t v) {
return (static_cast<uint32_t>(v) << 1U) ^
static_cast<uint32_t>(
-static_cast<int32_t>(static_cast<uint32_t>(v) >> 31U));
}
[[nodiscard]] IGUANA_INLINE uint64_t encode_zigzag(int64_t v) {
return (static_cast<uint64_t>(v) << 1U) ^
static_cast<uint64_t>(
-static_cast<int64_t>(static_cast<uint64_t>(v) >> 63U));
}
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint64_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint32_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
template <class T>
IGUANA_INLINE uint64_t decode_varint(T& data, size_t& pos) {
const int8_t* begin = reinterpret_cast<const int8_t*>(data.data());
const int8_t* end = begin + data.size();
const int8_t* p = begin;
uint64_t val = 0;
if ((static_cast<uint64_t>(*p) & 0x80) == 0) {
pos = 1;
return static_cast<uint64_t>(*p);
}
// end is always greater than or equal to begin, so this subtraction is safe
if (size_t(end - begin) >= 10)
IGUANA_LIKELY { // fast path
int64_t b;
do {
b = *p++;
val = (b & 0x7f);
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 7;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 14;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 21;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 28;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 35;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 42;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 49;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x7f) << 56;
if (b >= 0) {
break;
}
b = *p++;
val |= (b & 0x01) << 63;
if (b >= 0) {
break;
}
throw std::invalid_argument("Invalid varint value: too many bytes.");
} while (false);
}
else {
int shift = 0;
while (p != end && *p < 0) {
val |= static_cast<uint64_t>(*p++ & 0x7f) << shift;
shift += 7;
}
if (p == end)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid varint value: too few bytes.");
}
val |= static_cast<uint64_t>(*p++) << shift;
}
pos = (p - begin);
return val;
}
// value == 0 ? 1 : floor(log2(value)) / 7 + 1
constexpr size_t variant_uint32_size_constexpr(uint32_t value) {
if (value == 0) {
return 1;
}
int log = 0;
while (value >>= 1) ++log;
return log / 7 + 1;
}
template <uint64_t v, size_t I, typename It>
IGUANA_INLINE void append_varint_u32_constexpr_help(It&& it) {
*(it++) = static_cast<uint8_t>((v >> (7 * I)) | 0x80);
}
template <uint64_t v, typename It, size_t... I>
IGUANA_INLINE void append_varint_u32_constexpr(It&& it,
std::index_sequence<I...>) {
(append_varint_u32_constexpr_help<v, I>(it), ...);
}
template <uint32_t v, typename It>
IGUANA_INLINE void serialize_varint_u32_constexpr(It&& it) {
constexpr auto size = variant_uint32_size_constexpr(v);
append_varint_u32_constexpr<v>(it, std::make_index_sequence<size - 1>{});
*(it++) = static_cast<uint8_t>(v >> (7 * (size - 1)));
}
template <typename It>
IGUANA_INLINE void serialize_varint(uint64_t v, It&& it) {
if (v < 0x80) {
*(it++) = static_cast<uint8_t>(v);
return;
}
*(it++) = static_cast<uint8_t>(v | 0x80);
v >>= 7;
if (v < 0x80) {
*(it++) = static_cast<uint8_t>(v);
return;
}
do {
*(it++) = static_cast<uint8_t>(v | 0x80);
v >>= 7;
} while (v >= 0x80);
*(it++) = static_cast<uint8_t>(v);
}
IGUANA_INLINE uint32_t log2_floor_uint32(uint32_t n) {
#if defined(__GNUC__)
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
#else
unsigned long where;
_BitScanReverse(&where, n);
return where;
#endif
}
IGUANA_INLINE size_t variant_uint32_size(uint32_t value) {
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
// Use an explicit multiplication to implement the divide of
// a number in the 1..31 range.
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
// undefined.
uint32_t log2value = log2_floor_uint32(value | 0x1);
return static_cast<size_t>((log2value * 9 + 73) / 64);
}
IGUANA_INLINE int Log2FloorNonZero_Portable(uint32_t n) {
if (n == 0)
return -1;
int log = 0;
uint32_t value = n;
for (int i = 4; i >= 0; --i) {
int shift = (1 << i);
uint32_t x = value >> shift;
if (x != 0) {
value = x;
log += shift;
}
}
assert(value == 1);
return log;
}
IGUANA_INLINE uint32_t Log2FloorNonZero(uint32_t n) {
#if defined(__GNUC__)
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
#elif defined(_MSC_VER)
unsigned long where;
_BitScanReverse(&where, n);
return where;
#else
return Log2FloorNonZero_Portable(n);
#endif
}
IGUANA_INLINE int Log2FloorNonZero64_Portable(uint64_t n) {
const uint32_t topbits = static_cast<uint32_t>(n >> 32);
if (topbits == 0) {
// Top bits are zero, so scan in bottom bits
return static_cast<int>(Log2FloorNonZero(static_cast<uint32_t>(n)));
}
else {
return 32 + static_cast<int>(Log2FloorNonZero(topbits));
}
}
IGUANA_INLINE uint32_t log2_floor_uint64(uint64_t n) {
#if defined(__GNUC__)
return 63 ^ static_cast<uint32_t>(__builtin_clzll(n));
#elif defined(_MSC_VER) && defined(_M_X64)
unsigned long where;
_BitScanReverse64(&where, n);
return where;
#else
return Log2FloorNonZero64_Portable(n);
#endif
}
IGUANA_INLINE size_t variant_uint64_size(uint64_t value) {
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
// Use an explicit multiplication to implement the divide of
// a number in the 1..63 range.
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
// undefined.
uint32_t log2value = log2_floor_uint64(value | 0x1);
return static_cast<size_t>((log2value * 9 + 73) / 64);
}
template <typename U>
constexpr IGUANA_INLINE size_t variant_intergal_size(U value) {
using T = std::remove_reference_t<U>;
if constexpr (sizeof(T) == 8) {
return variant_uint64_size(static_cast<uint64_t>(value));
}
else if constexpr (sizeof(T) <= 4) {
if constexpr (std::is_signed_v<T>) {
if (value < 0) {
return 10;
}
}
return variant_uint32_size(static_cast<uint32_t>(value));
}
else {
static_assert(!sizeof(T), "intergal in not supported");
}
}
template <typename F, size_t... I>
IGUANA_INLINE constexpr void for_each_n(F&& f, std::index_sequence<I...>) {
(std::forward<F>(f)(std::integral_constant<size_t, I>{}), ...);
}
template <size_t key_size, bool omit_default_val, typename Type>
IGUANA_INLINE size_t str_numeric_size(Type&& t) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
// string
if constexpr (omit_default_val) {
if (t.size() == 0)
IGUANA_UNLIKELY { return 0; }
}
if constexpr (key_size == 0) {
return t.size();
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(t.size())) +
t.size();
}
}
else {
// numeric
if constexpr (omit_default_val) {
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
if (t.val == 0)
IGUANA_UNLIKELY { return 0; }
}
else {
if (t == static_cast<T>(0))
IGUANA_UNLIKELY { return 0; }
}
}
if constexpr (std::is_integral_v<T>) {
if constexpr (std::is_same_v<bool, T>) {
return 1 + key_size;
}
else {
return key_size + variant_intergal_size(t);
}
}
else if constexpr (detail::is_signed_varint_v<T>) {
return key_size + variant_intergal_size(encode_zigzag(t.val));
}
else if constexpr (detail::is_fixed_v<T>) {
return key_size + sizeof(typename T::value_type);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
return key_size + sizeof(T);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
return key_size + variant_intergal_size(static_cast<U>(t));
}
else {
static_assert(!sizeof(T), "err");
}
}
}
template <size_t key_size, bool omit_default_val = true, typename Type,
typename Arr>
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr);
template <typename Variant, typename T, size_t I>
constexpr inline size_t get_variant_index() {
if constexpr (I == 0) {
static_assert(std::is_same_v<std::variant_alternative_t<0, Variant>, T>,
"Type T is not found in Variant");
return 0;
}
else if constexpr (std::is_same_v<std::variant_alternative_t<I, Variant>,
T>) {
return I;
}
else {
return get_variant_index<Variant, T, I - 1>();
}
}
template <size_t field_no, typename Type, typename Arr>
IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr& size_arr) {
using T = std::decay_t<Type>;
int len = 0;
std::visit(
[&len, &size_arr](auto&& value) IGUANA__INLINE_LAMBDA {
using value_type =
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
constexpr auto offset =
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
constexpr uint32_t key =
((field_no + offset) << 3) |
static_cast<uint32_t>(get_wire_type<value_type>());
len = pb_key_value_size<variant_uint32_size_constexpr(key), false>(
std::forward<value_type>(value), size_arr);
},
std::forward<Type>(t));
return len;
}
// returns size = key_size + optional(len_size) + len
// when key_size == 0, return len
template <size_t key_size, bool omit_default_val, typename Type, typename Arr>
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (is_reflection_v<T> || is_custom_reflection_v<T>) {
size_t len = 0;
static constexpr auto tuple = get_members_tuple<T>();
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
size_t pre_index = -1;
if constexpr (!inherits_from_base_v<T> && key_size != 0) {
pre_index = size_arr.size();
size_arr.push_back(0); // placeholder
}
for_each_n(
[&len, &t, &size_arr](auto i) IGUANA__INLINE_LAMBDA {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
constexpr auto value = std::get<decltype(i)::value>(tuple);
using U = typename field_type::value_type;
auto& val = value.value(t);
if constexpr (variant_v<U>) {
constexpr auto offset =
get_variant_index<U, typename field_type::sub_type,
std::variant_size_v<U> - 1>();
if constexpr (offset == 0) {
len += pb_oneof_size<value.field_no>(val, size_arr);
}
}
else {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<U>());
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
len += pb_key_value_size<sub_keysize>(val, size_arr);
}
},
std::make_index_sequence<SIZE>{});
if constexpr (inherits_from_base_v<T>) {
t.cache_size = len;
}
else if constexpr (key_size != 0) {
size_arr[pre_index] = len;
}
if constexpr (key_size == 0) {
// for top level
return len;
}
else {
if (len == 0) {
// equals key_size + variant_uint32_size(len)
return key_size + 1;
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
size_t len = 0;
if constexpr (is_lenprefix_v<item_type>) {
for (auto& item : t) {
len += pb_key_value_size<key_size, false>(item, size_arr);
}
return len;
}
else {
for (auto& item : t) {
// here 0 to get pakced size, and item must be numeric
len += str_numeric_size<0, false>(item);
}
if (len == 0) {
return 0;
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
}
else if constexpr (is_map_container<T>::value) {
size_t len = 0;
for (auto& [k, v] : t) {
// the key_size of k and v is constant 1
auto kv_len = pb_key_value_size<1, false>(k, size_arr) +
pb_key_value_size<1, false>(v, size_arr);
len += key_size + variant_uint32_size(static_cast<uint32_t>(kv_len)) +
kv_len;
}
return len;
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_key_value_size<key_size, omit_default_val>(*t, size_arr);
}
else {
return str_numeric_size<key_size, omit_default_val>(t);
}
}
// return the payload size
template <bool skip_next = true, typename Type>
IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (is_reflection_v<T> || is_custom_reflection_v<T>) {
if constexpr (inherits_from_base_v<T>) {
return t.cache_size;
}
else {
// *sz_ptr is secure and logically guaranteed
if constexpr (skip_next) {
return *(sz_ptr++);
}
else {
return *sz_ptr;
}
}
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
size_t len = 0;
if constexpr (!is_lenprefix_v<item_type>) {
for (auto& item : t) {
len += str_numeric_size<0, false>(item);
}
return len;
}
else {
static_assert(!sizeof(item_type), "the size of this type is meaningless");
}
}
else if constexpr (is_map_container<T>::value) {
static_assert(!sizeof(T), "the size of this type is meaningless");
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_value_size(*t, sz_ptr);
}
else {
return str_numeric_size<0, false>(t);
}
}
} // namespace detail
} // namespace iguana

View File

@ -0,0 +1,219 @@
#pragma once
#include "detail/string_resize.hpp"
#include "pb_util.hpp"
namespace iguana {
namespace detail {
template <uint32_t key, typename V, typename It>
IGUANA_INLINE void encode_varint_field(V val, It&& it) {
static_assert(std::is_integral_v<V>, "must be integral");
if constexpr (key != 0) {
serialize_varint_u32_constexpr<key>(it);
}
serialize_varint(val, it);
}
template <uint32_t key, typename V, typename It>
IGUANA_INLINE void encode_fixed_field(V val, It&& it) {
if constexpr (key != 0) {
serialize_varint_u32_constexpr<key>(it);
}
constexpr size_t size = sizeof(V);
// TODO: check Stream continuous
memcpy(it, &val, size);
it += size;
}
template <uint32_t key, bool omit_default_val = true, typename Type,
typename It>
IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr);
template <uint32_t key, typename V, typename It>
IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size,
uint32_t*& sz_ptr) {
if (size == 0)
IGUANA_UNLIKELY {
// map keys can't be omitted even if values are empty
// TODO: repeated ?
serialize_varint_u32_constexpr<key>(it);
serialize_varint(0, it);
}
else {
to_pb_impl<key, false>(val, it, sz_ptr);
}
}
template <uint32_t key, bool omit_default_val, typename T, typename It>
IGUANA_INLINE void encode_numeric_field(T t, It&& it) {
if constexpr (omit_default_val) {
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
if (t.val == 0) {
return;
}
}
else {
if (t == static_cast<T>(0))
IGUANA_UNLIKELY { return; }
}
}
if constexpr (std::is_integral_v<T>) {
detail::encode_varint_field<key>(t, it);
}
else if constexpr (detail::is_signed_varint_v<T>) {
detail::encode_varint_field<key>(encode_zigzag(t.val), it);
}
else if constexpr (detail::is_fixed_v<T>) {
detail::encode_fixed_field<key>(t.val, it);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
detail::encode_fixed_field<key>(t, it);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
detail::encode_varint_field<key>(static_cast<U>(t), it);
}
else {
static_assert(!sizeof(T), "unsupported type");
}
}
template <uint32_t field_no, typename Type, typename It>
IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it, uint32_t*& sz_ptr) {
using T = std::decay_t<Type>;
std::visit(
[&it, &sz_ptr](auto&& value) IGUANA__INLINE_LAMBDA {
using value_type =
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
constexpr auto offset =
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
constexpr uint32_t key =
((field_no + offset) << 3) |
static_cast<uint32_t>(get_wire_type<value_type>());
to_pb_impl<key, false>(std::forward<value_type>(value), it, sz_ptr);
},
std::forward<Type>(t));
}
// omit_default_val = true indicates to omit the default value in searlization
template <uint32_t key, bool omit_default_val, typename Type, typename It>
IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (is_reflection_v<T> || is_custom_reflection_v<T>) {
// can't be omitted even if values are empty
if constexpr (key != 0) {
auto len = pb_value_size(t, sz_ptr);
serialize_varint_u32_constexpr<key>(it);
serialize_varint(len, it);
if (len == 0)
IGUANA_UNLIKELY { return; }
}
static constexpr auto tuple = get_members_tuple<T>();
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
for_each_n(
[&t, &it, &sz_ptr](auto i) IGUANA__INLINE_LAMBDA {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
constexpr auto value = std::get<decltype(i)::value>(tuple);
auto& val = value.value(t);
using U = typename field_type::value_type;
if constexpr (variant_v<U>) {
constexpr auto offset =
get_variant_index<U, typename field_type::sub_type,
std::variant_size_v<U> - 1>();
if constexpr (offset == 0) {
to_pb_oneof<value.field_no>(val, it, sz_ptr);
}
}
else {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<U>());
to_pb_impl<sub_key>(val, it, sz_ptr);
}
},
std::make_index_sequence<SIZE>{});
}
else if constexpr (is_sequence_container<T>::value) {
// TODO support std::array
// repeated values can't be omitted even if values are empty
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// non-packed
for (auto& item : t) {
to_pb_impl<key, false>(item, it, sz_ptr);
}
}
else {
if (t.empty())
IGUANA_UNLIKELY { return; }
serialize_varint_u32_constexpr<key>(it);
serialize_varint(pb_value_size(t, sz_ptr), it);
for (auto& item : t) {
encode_numeric_field<false, 0>(item, it);
}
}
}
else if constexpr (is_map_container<T>::value) {
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;
constexpr uint32_t key1 =
(1 << 3) | static_cast<uint32_t>(get_wire_type<first_type>());
constexpr auto key1_size = variant_uint32_size_constexpr(key1);
constexpr uint32_t key2 =
(2 << 3) | static_cast<uint32_t>(get_wire_type<second_type>());
constexpr auto key2_size = variant_uint32_size_constexpr(key2);
for (auto& [k, v] : t) {
serialize_varint_u32_constexpr<key>(it);
// k must be string or numeric
auto k_val_len = str_numeric_size<0, false>(k);
auto v_val_len = pb_value_size<false>(v, sz_ptr);
auto pair_len = key1_size + key2_size + k_val_len + v_val_len;
if constexpr (is_lenprefix_v<first_type>) {
pair_len += variant_uint32_size(k_val_len);
}
if constexpr (is_lenprefix_v<second_type>) {
pair_len += variant_uint32_size(v_val_len);
}
serialize_varint(pair_len, it);
// map k and v can't be omitted even if values are empty
encode_pair_value<key1>(k, it, k_val_len, sz_ptr);
encode_pair_value<key2>(v, it, v_val_len, sz_ptr);
}
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_impl<key, omit_default_val>(*t, it, sz_ptr);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
if constexpr (omit_default_val) {
if (t.size() == 0)
IGUANA_UNLIKELY { return; }
}
serialize_varint_u32_constexpr<key>(it);
serialize_varint(t.size(), it);
memcpy(it, t.data(), t.size());
it += t.size();
}
else {
encode_numeric_field<key, omit_default_val>(t, it);
}
}
} // namespace detail
template <typename T, typename Stream>
IGUANA_INLINE void to_pb(T& t, Stream& out) {
std::vector<uint32_t> size_arr;
auto byte_len = detail::pb_key_value_size<0>(t, size_arr);
detail::resize(out, byte_len);
auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0];
detail::to_pb_impl<0>(t, &out[0], sz_ptr);
}
} // namespace iguana

View File

@ -9,6 +9,7 @@
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <memory>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -19,6 +20,7 @@
#include "detail/string_stream.hpp" #include "detail/string_stream.hpp"
#include "detail/traits.hpp" #include "detail/traits.hpp"
#include "enum_reflection.hpp"
#include "frozen/string.h" #include "frozen/string.h"
#include "frozen/unordered_map.h" #include "frozen/unordered_map.h"
@ -550,9 +552,88 @@ namespace iguana::detail {
#define MAKE_STR_LIST(...) \ #define MAKE_STR_LIST(...) \
MACRO_CONCAT(CON_STR, GET_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__) MACRO_CONCAT(CON_STR, GET_ARG_COUNT(__VA_ARGS__))(__VA_ARGS__)
template <typename T>
struct identity {};
struct field_info {
size_t offset;
std::string_view type_name;
};
struct base {
virtual void to_pb(std::string &str) {}
virtual void from_pb(std::string_view str) {}
virtual std::vector<std::string_view> get_fields_name() { return {}; }
virtual iguana::detail::field_info get_field_info(std::string_view name) {
return {};
}
template <typename T>
T &get_field_value(std::string_view name) {
auto info = get_field_info(name);
check_field<T>(name, info);
auto ptr = (((char *)this) + info.offset);
return *((T *)ptr);
}
template <typename T>
void set_field_value(std::string_view name, T val) {
auto info = get_field_info(name);
check_field<T>(name, info);
auto ptr = (((char *)this) + info.offset);
*((T *)ptr) = std::move(val);
}
virtual ~base() {}
private:
template <typename T>
void check_field(std::string_view name, const field_info &info) {
if (info.offset == 0) {
throw std::invalid_argument(std::string(name) + " field not exist ");
}
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
if (info.type_name != iguana::type_string<T>()) {
std::string str = "type is not match: can not assign ";
str.append(iguana::type_string<T>());
str.append(" to ").append(info.type_name);
throw std::invalid_argument(str);
}
#endif
}
};
inline std::unordered_map<std::string_view,
std::function<std::shared_ptr<base>()>>
g_pb_map;
template <typename T>
inline bool register_type() {
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
if constexpr (std::is_base_of_v<base, T>) {
auto it = g_pb_map.emplace(type_string<T>(), [] {
return std::make_shared<T>();
});
return it.second;
}
else {
return true;
}
#else
return true;
#endif
}
#define MAKE_META_DATA_IMPL(STRUCT_NAME, ...) \ #define MAKE_META_DATA_IMPL(STRUCT_NAME, ...) \
static inline bool IGUANA_UNIQUE_VARIABLE(reg_var) = \
iguana::detail::register_type<STRUCT_NAME>(); \
[[maybe_unused]] inline static auto iguana_reflect_members( \ [[maybe_unused]] inline static auto iguana_reflect_members( \
STRUCT_NAME const &) { \ const iguana::detail::identity<STRUCT_NAME> &) { \
struct reflect_members { \ struct reflect_members { \
constexpr decltype(auto) static apply_impl() { \ constexpr decltype(auto) static apply_impl() { \
return std::make_tuple(__VA_ARGS__); \ return std::make_tuple(__VA_ARGS__); \
@ -602,7 +683,7 @@ constexpr std::array<frozen::string, N> get_alias_arr(Args... pairs) {
#define MAKE_META_DATA_IMPL_ALIAS(STRUCT_NAME, ALIAS, ...) \ #define MAKE_META_DATA_IMPL_ALIAS(STRUCT_NAME, ALIAS, ...) \
[[maybe_unused]] inline static auto iguana_reflect_members( \ [[maybe_unused]] inline static auto iguana_reflect_members( \
STRUCT_NAME const &) { \ const iguana::detail::identity<STRUCT_NAME> &) { \
struct reflect_members { \ struct reflect_members { \
constexpr decltype(auto) static apply_impl() { \ constexpr decltype(auto) static apply_impl() { \
return iguana::detail::get_mem_ptr_tp(__VA_ARGS__); \ return iguana::detail::get_mem_ptr_tp(__VA_ARGS__); \
@ -622,7 +703,8 @@ constexpr std::array<frozen::string, N> get_alias_arr(Args... pairs) {
} }
#define MAKE_META_DATA_IMPL_EMPTY(STRUCT_NAME) \ #define MAKE_META_DATA_IMPL_EMPTY(STRUCT_NAME) \
inline auto iguana_reflect_members(STRUCT_NAME const &) { \ inline auto iguana_reflect_members( \
const iguana::detail::identity<STRUCT_NAME> &) { \
struct reflect_members { \ struct reflect_members { \
constexpr decltype(auto) static apply_impl() { \ constexpr decltype(auto) static apply_impl() { \
return std::make_tuple(); \ return std::make_tuple(); \
@ -692,6 +774,143 @@ inline constexpr auto get_iguana_struct_map() {
} }
} }
template <typename T,
typename ElementType = typename member_traits<T>::value_type>
struct field_t {
using member_type = T;
using owner_type = typename member_traits<T>::owner_type;
using value_type = typename member_traits<T>::value_type;
using sub_type = ElementType;
constexpr field_t() = default;
constexpr field_t(T member, uint32_t number, frozen::string name = "")
: member_ptr(member), field_name(name), field_no(number) {}
T member_ptr;
frozen::string field_name;
uint32_t field_no;
auto &value(owner_type &value) const { return value.*member_ptr; }
};
template <typename T>
struct field_type_t;
template <typename... Args>
struct field_type_t<std::tuple<Args...>> {
using value_type = std::variant<Args...>;
};
template <typename T, typename = void>
struct is_custom_reflection : std::false_type {};
template <typename T>
struct is_custom_reflection<
T, std::void_t<decltype(get_members_impl(std::declval<T *>()))>>
: std::true_type {};
template <typename T, typename = void>
struct is_reflection : std::false_type {};
template <typename T>
inline constexpr bool is_reflection_v = is_reflection<T>::value;
template <typename T>
inline constexpr bool is_custom_reflection_v = is_custom_reflection<T>::value;
template <typename T, typename S, size_t... I>
constexpr inline auto build_variant_fields(T t, S &s, uint32_t base_idx,
std::index_sequence<I...>) {
using value_type = typename member_traits<T>::value_type;
return std::tuple(field_t<T, std::variant_alternative_t<I, value_type>>{
t, (base_idx + uint32_t(I)), s}...);
}
template <uint32_t I, typename T, typename S>
constexpr inline auto build_fields(T t, S &s, uint32_t &index) {
using value_type = typename member_traits<T>::value_type;
if constexpr (is_variant<value_type>::value) {
constexpr uint32_t Size = std::variant_size_v<value_type>;
index += (Size - 1);
return build_variant_fields(t, s, I + 1, std::make_index_sequence<Size>{});
}
else {
uint32_t field_no = (I == index) ? (I + 1) : (I + index);
index++;
return std::tuple(field_t{t, field_no, s});
}
}
template <typename T, typename U, size_t... I>
constexpr inline auto get_members_tuple_impl(T &&tp, U &&arr,
std::index_sequence<I...> &&) {
uint32_t index = 0;
return std::tuple_cat(build_fields<I>(std::get<I>(tp), arr[I], index)...);
}
template <typename T>
constexpr inline auto get_members_tuple() {
if constexpr (is_reflection<T>::value) {
using reflect_members = decltype(iguana_reflect_type(std::declval<T>()));
using Tuple = decltype(reflect_members::apply_impl());
constexpr size_t Size = std::tuple_size_v<Tuple>;
return get_members_tuple_impl(reflect_members::apply_impl(),
reflect_members::arr(),
std::make_index_sequence<Size>{});
}
else if constexpr (is_custom_reflection_v<T>) {
using U = std::remove_const_t<std::remove_reference_t<T>>;
return get_members_impl((U *)nullptr);
}
else {
static_assert(!sizeof(T), "expected reflection or custom reflection");
}
}
template <typename T, size_t Size, typename Tuple, size_t... I>
constexpr auto inline get_members_impl(Tuple &&tp, std::index_sequence<I...>) {
return frozen::unordered_map<uint32_t, T, sizeof...(I)>{
{std::get<I>(tp).field_no,
T{std::in_place_index<I>, std::move(std::get<I>(tp))}}...};
}
template <typename T>
constexpr size_t count_variant_size() {
if constexpr (is_variant<T>::value) {
return std::variant_size_v<T>;
}
else {
return 1;
}
}
template <typename T, size_t... I>
constexpr size_t tuple_type_count_impl(std::index_sequence<I...>) {
return (
(count_variant_size<member_value_type_t<std::tuple_element_t<I, T>>>() +
...));
}
template <typename T>
constexpr size_t tuple_type_count() {
return tuple_type_count_impl<T>(
std::make_index_sequence<std::tuple_size_v<T>>{});
}
template <typename T>
constexpr inline auto get_members() {
if constexpr (is_reflection_v<T> || is_custom_reflection_v<T>) {
constexpr auto tp = get_members_tuple<T>();
using Tuple = std::decay_t<decltype(tp)>;
using value_type = typename field_type_t<Tuple>::value_type;
constexpr auto Size = tuple_type_count<Tuple>();
return get_members_impl<value_type, Size>(tp,
std::make_index_sequence<Size>{});
}
else {
static_assert(!sizeof(T), "expected reflection or custom reflection");
}
}
#define REFLECTION(STRUCT_NAME, ...) \ #define REFLECTION(STRUCT_NAME, ...) \
MAKE_META_DATA(STRUCT_NAME, #STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), \ MAKE_META_DATA(STRUCT_NAME, #STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), \
__VA_ARGS__) __VA_ARGS__)
@ -703,11 +922,7 @@ inline constexpr auto get_iguana_struct_map() {
STRUCT_NAME, ALIAS, \ STRUCT_NAME, ALIAS, \
std::tuple_size_v<decltype(std::make_tuple(__VA_ARGS__))>, __VA_ARGS__) std::tuple_size_v<decltype(std::make_tuple(__VA_ARGS__))>, __VA_ARGS__)
#ifdef _MSC_VER
#define IGUANA_UNIQUE_VARIABLE(str) MACRO_CONCAT(str, __COUNTER__) #define IGUANA_UNIQUE_VARIABLE(str) MACRO_CONCAT(str, __COUNTER__)
#else
#define IGUANA_UNIQUE_VARIABLE(str) MACRO_CONCAT(str, __LINE__)
#endif
template <typename T> template <typename T>
struct iguana_required_struct; struct iguana_required_struct;
#define REQUIRED_IMPL(STRUCT_NAME, N, ...) \ #define REQUIRED_IMPL(STRUCT_NAME, N, ...) \
@ -762,12 +977,6 @@ inline int add_custom_fields(std::string_view key,
return 0; return 0;
} }
#ifdef _MSC_VER
#define IGUANA_UNIQUE_VARIABLE(str) MACRO_CONCAT(str, __COUNTER__)
#else
#define IGUANA_UNIQUE_VARIABLE(str) MACRO_CONCAT(str, __LINE__)
#endif
#define CUSTOM_FIELDS_IMPL(STRUCT_NAME, N, ...) \ #define CUSTOM_FIELDS_IMPL(STRUCT_NAME, N, ...) \
inline auto IGUANA_UNIQUE_VARIABLE(STRUCT_NAME) = iguana::add_custom_fields( \ inline auto IGUANA_UNIQUE_VARIABLE(STRUCT_NAME) = iguana::add_custom_fields( \
#STRUCT_NAME, {MARCO_EXPAND(MACRO_CONCAT(CON_STR, N)(__VA_ARGS__))}); #STRUCT_NAME, {MARCO_EXPAND(MACRO_CONCAT(CON_STR, N)(__VA_ARGS__))});
@ -776,14 +985,16 @@ inline int add_custom_fields(std::string_view key,
CUSTOM_FIELDS_IMPL(STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) CUSTOM_FIELDS_IMPL(STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)
template <typename T> template <typename T>
using Reflect_members = decltype(iguana_reflect_members(std::declval<T>())); using Reflect_members = decltype(iguana_reflect_members(
std::declval<iguana::detail::identity<T>>()));
template <typename T, typename = void> template <typename T, typename = void>
struct is_public_reflection : std::false_type {}; struct is_public_reflection : std::false_type {};
template <typename T> template <typename T>
struct is_public_reflection< struct is_public_reflection<T,
T, std::void_t<decltype(iguana_reflect_members(std::declval<T>()))>> std::void_t<decltype(iguana_reflect_members(
std::declval<iguana::detail::identity<T>>()))>>
: std::true_type {}; : std::true_type {};
template <typename T> template <typename T>
@ -795,14 +1006,11 @@ struct is_private_reflection : std::false_type {};
template <typename T> template <typename T>
struct is_private_reflection< struct is_private_reflection<
T, std::void_t<decltype(std::declval<T>().iguana_reflect_members( T, std::void_t<decltype(std::declval<T>().iguana_reflect_members(
std::declval<T>()))>> : std::true_type {}; std::declval<iguana::detail::identity<T>>()))>> : std::true_type {};
template <typename T> template <typename T>
constexpr bool is_private_reflection_v = is_private_reflection<T>::value; constexpr bool is_private_reflection_v = is_private_reflection<T>::value;
template <typename T, typename = void>
struct is_reflection : std::false_type {};
template <typename T> template <typename T>
struct is_reflection<T, std::enable_if_t<is_private_reflection_v<T>>> struct is_reflection<T, std::enable_if_t<is_private_reflection_v<T>>>
: std::true_type {}; : std::true_type {};
@ -813,17 +1021,14 @@ struct is_reflection<T, std::enable_if_t<is_public_reflection_v<T>>>
template <typename T> template <typename T>
inline auto iguana_reflect_type(const T &t) { inline auto iguana_reflect_type(const T &t) {
if constexpr (is_public_reflection_v<T>) { if constexpr (is_public_reflection_v<std::decay_t<T>>) {
return iguana_reflect_members(t); return iguana_reflect_members(iguana::detail::identity<T>{});
} }
else { else {
return t.iguana_reflect_members(t); return t.iguana_reflect_members(iguana::detail::identity<T>{});
} }
} }
template <typename T>
inline constexpr bool is_reflection_v = is_reflection<T>::value;
template <std::size_t index, template <typename...> typename Condition, template <std::size_t index, template <typename...> typename Condition,
typename Tuple, typename Owner> typename Tuple, typename Owner>
constexpr int element_index_helper() { constexpr int element_index_helper() {
@ -1112,7 +1317,8 @@ constexpr void for_each(const std::tuple<Args...> &t, F &&f,
} }
template <typename T, typename F> template <typename T, typename F>
constexpr std::enable_if_t<is_reflection<T>::value> for_each(T &&t, F &&f) { constexpr std::enable_if_t<is_reflection<std::decay_t<T>>::value> for_each(
T &&t, F &&f) {
using M = decltype(iguana_reflect_type(std::forward<T>(t))); using M = decltype(iguana_reflect_type(std::forward<T>(t)));
for_each(M::apply_impl(), std::forward<F>(f), for_each(M::apply_impl(), std::forward<F>(f),
std::make_index_sequence<M::value()>{}); std::make_index_sequence<M::value()>{});

View File

@ -130,12 +130,6 @@ constexpr inline bool shared_ptr_v =
template <typename T> template <typename T>
constexpr inline bool smart_ptr_v = shared_ptr_v<T> || unique_ptr_v<T>; constexpr inline bool smart_ptr_v = shared_ptr_v<T> || unique_ptr_v<T>;
template <typename T>
struct is_variant : std::false_type {};
template <typename... T>
struct is_variant<std::variant<T...>> : std::true_type {};
template <typename T> template <typename T>
constexpr inline bool variant_v = is_variant<std::remove_cvref_t<T>>::value; constexpr inline bool variant_v = is_variant<std::remove_cvref_t<T>>::value;

View File

@ -5,4 +5,4 @@
// IGUANA_VERSION % 100 is the sub-minor version // IGUANA_VERSION % 100 is the sub-minor version
// IGUANA_VERSION / 100 % 1000 is the minor version // IGUANA_VERSION / 100 % 1000 is the minor version
// IGUANA_VERSION / 100000 is the major version // IGUANA_VERSION / 100000 is the major version
#define IGUANA_VERSION 100004 // 1.0.4 #define IGUANA_VERSION 100005 // 1.0.5

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2023, Alibaba Group Holding Limited; * Copyright (c) 2024, Alibaba Group Holding Limited;
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,89 +14,4 @@
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <cassert> #include "struct_pb/struct_pb_impl.hpp"
#include <cstdint>
#include <cstring>
#include <vector>
#if defined __clang__
#define STRUCT_PB_INLINE __attribute__((always_inline)) inline
#elif defined _MSC_VER
#define STRUCT_PB_INLINE __forceinline
#else
#define STRUCT_PB_INLINE __attribute__((always_inline)) inline
#endif
#define STRUCT_PB_NODISCARD [[nodiscard]]
namespace struct_pb {
struct UnknownFields {
STRUCT_PB_NODISCARD std::size_t total_size() const {
std::size_t total = 0;
for (const auto& f : fields) {
total += f.len;
}
return total;
}
void serialize_to(char* data, std::size_t& pos, std::size_t size) const {
for (const auto& f : fields) {
assert(pos + f.len <= size);
std::memcpy(data + pos, f.data, f.len);
pos += f.len;
}
}
void add_field(const char* data, std::size_t start, std::size_t end) {
fields.push_back(Field{data + start, end - start});
}
struct Field {
const char* data;
std::size_t len;
};
std::vector<Field> fields;
};
/*
* Low-Level API for struct_pb user
* the following internal API will be generated by struct_pb protoc plugin
*/
namespace internal {
template <typename T>
STRUCT_PB_NODISCARD std::size_t get_needed_size(
const T& t, const UnknownFields& unknown_fields = {});
template <typename T>
void serialize_to(char* data, std::size_t size, const T& t,
const UnknownFields& unknown_fields = {});
template <typename T>
STRUCT_PB_NODISCARD bool deserialize_to(T& t, const char* data,
std::size_t size,
UnknownFields& unknown_fields);
template <typename T>
STRUCT_PB_NODISCARD bool deserialize_to(T& t, const char* data,
std::size_t size);
} // namespace internal
/*
* High-Level API for struct_pb user
* If you need more fine-grained operations, encapsulate the internal API
* yourself.
*/
template <typename Buffer = std::vector<char>, typename T>
STRUCT_PB_NODISCARD Buffer serialize(const T& t,
const UnknownFields& unknown_fields = {}) {
Buffer buffer;
auto size = struct_pb::internal::get_needed_size(t, unknown_fields);
buffer.resize(size);
struct_pb::internal::serialize_to(buffer.data(), buffer.size(), t,
unknown_fields);
return buffer;
}
template <typename T, typename Buffer>
STRUCT_PB_NODISCARD STRUCT_PB_INLINE bool deserialize_to(
T& t, UnknownFields& unknown_fields, const Buffer& buffer) {
return struct_pb::internal::deserialize_to(t, buffer.data(), buffer.size(),
unknown_fields);
}
template <typename T, typename Buffer>
STRUCT_PB_NODISCARD STRUCT_PB_INLINE bool deserialize_to(T& t,
const Buffer& buffer) {
return struct_pb::internal::deserialize_to(t, buffer.data(), buffer.size());
}
} // namespace struct_pb

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2023, Alibaba Group Holding Limited; * Copyright (c) 2024, Alibaba Group Holding Limited;
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,171 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <cassert> #include <iguana/pb_reader.hpp>
#include <cstring> #include <iguana/pb_writer.hpp>
#include <map> namespace struct_pb = iguana;
#include <string>
#include <vector>
#include "ylt/struct_pb.hpp"
namespace struct_pb {
namespace internal {
STRUCT_PB_NODISCARD STRUCT_PB_INLINE uint32_t encode_zigzag(int32_t v) {
return (static_cast<uint32_t>(v) << 1U) ^
static_cast<uint32_t>(
-static_cast<int32_t>(static_cast<uint32_t>(v) >> 31U));
}
STRUCT_PB_NODISCARD STRUCT_PB_INLINE uint64_t encode_zigzag(int64_t v) {
return (static_cast<uint64_t>(v) << 1U) ^
static_cast<uint64_t>(
-static_cast<int64_t>(static_cast<uint64_t>(v) >> 63U));
}
STRUCT_PB_NODISCARD STRUCT_PB_INLINE int64_t decode_zigzag(uint64_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
STRUCT_PB_NODISCARD STRUCT_PB_INLINE int64_t decode_zigzag(uint32_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
STRUCT_PB_NODISCARD STRUCT_PB_INLINE std::size_t calculate_varint_size(
uint64_t v) {
std::size_t ret = 0;
do {
ret++;
v >>= 7;
} while (v != 0);
return ret;
}
[[nodiscard]] STRUCT_PB_INLINE bool decode_varint(const char* data,
std::size_t& pos_,
std::size_t size_,
uint64_t& v) {
// fix test failed on arm due to different char definition
const signed char* data_ = reinterpret_cast<const signed char*>(data);
// from https://github.com/facebook/folly/blob/main/folly/Varint.h
if (pos_ < size_ && (static_cast<uint64_t>(data_[pos_]) & 0x80U) == 0) {
v = static_cast<uint64_t>(data_[pos_]);
pos_++;
return true;
}
constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
uint64_t val = 0;
if (size_ - pos_ >= max_varint_length) [[likely]] {
do {
// clang-format off
int64_t b = data_[pos_++];
val = ((uint64_t(b) & 0x7fU) ); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 7U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 14U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 21U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 28U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 35U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 42U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 49U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 56U); if (b >= 0) { break; }
b = data_[pos_++]; val |= ((uint64_t(b) & 0x01U) << 63U); if (b >= 0) { break; }
// clang-format on
return false;
} while (false);
}
else {
unsigned int shift = 0;
while (pos_ != size_ && int64_t(data_[pos_]) < 0) {
val |= (uint64_t(data_[pos_++]) & 0x7fU) << shift;
shift += 7;
}
if (pos_ == size_) {
return false;
}
val |= uint64_t(data_[pos_++]) << shift;
}
v = val;
return true;
}
STRUCT_PB_INLINE void serialize_varint(char* const data, std::size_t& pos,
std::size_t size, uint64_t v) {
while (v >= 0x80) {
assert(pos < size);
data[pos++] = static_cast<uint8_t>(v | 0x80);
v >>= 7;
}
data[pos++] = static_cast<uint8_t>(v);
}
STRUCT_PB_NODISCARD STRUCT_PB_INLINE bool deserialize_varint(const char* data,
std::size_t& pos,
std::size_t size,
uint64_t& v) {
return decode_varint(data, pos, size, v);
}
STRUCT_PB_NODISCARD STRUCT_PB_INLINE bool read_tag(const char* data,
std::size_t& pos,
std::size_t size,
uint64_t& tag) {
return deserialize_varint(data, pos, size, tag);
}
inline bool deserialize_unknown(const char* data, std::size_t& pos,
std::size_t size, uint32_t tag,
UnknownFields& unknown_fields) {
uint32_t field_number = tag >> 3;
if (field_number == 0) {
return false;
}
auto offset = internal::calculate_varint_size(tag);
auto start = pos - offset;
uint32_t wire_type = tag & 0b0000'0111;
switch (wire_type) {
case 0: {
uint64_t t;
auto ok = internal::deserialize_varint(data, pos, size, t);
if (!ok) [[unlikely]] {
return false;
}
unknown_fields.add_field(data, start, pos);
break;
}
case 1: {
static_assert(sizeof(double) == 8);
if (pos + 8 > size) [[unlikely]] {
return false;
}
pos += 8;
unknown_fields.add_field(data, start, pos);
break;
}
case 2: {
uint64_t sz;
auto ok = internal::deserialize_varint(data, pos, size, sz);
if (!ok) [[unlikely]] {
return false;
}
if (pos + sz > size) [[unlikely]] {
return false;
}
pos += sz;
unknown_fields.add_field(data, start, pos);
break;
}
case 5: {
static_assert(sizeof(float) == 4);
if (pos + 4 > size) [[unlikely]] {
return false;
}
pos += 4;
unknown_fields.add_field(data, start, pos);
break;
}
default: {
assert(false && "error path");
}
}
return true;
}
} // namespace internal
} // namespace struct_pb

View File

@ -10,6 +10,7 @@ cc_binary(
"no_op.cpp", "no_op.cpp",
"no_op.h", "no_op.h",
"sample.hpp", "sample.hpp",
"struct_pb_sample.hpp",
"struct_pack_sample.hpp", "struct_pack_sample.hpp",
"msgpack_sample.hpp" "msgpack_sample.hpp"
], ],

View File

@ -15,19 +15,6 @@ if (Protobuf_FOUND)
target_link_libraries(struct_pack_benchmark PRIVATE protobuf::libprotobuf) target_link_libraries(struct_pack_benchmark PRIVATE protobuf::libprotobuf)
target_compile_definitions(struct_pack_benchmark PRIVATE HAVE_PROTOBUF) target_compile_definitions(struct_pack_benchmark PRIVATE HAVE_PROTOBUF)
if (BUILD_STRUCT_PB)
protobuf_generate_struct_pb(STRUCT_PACK_BENCHMARK_PROTO_SRCS2
STRUCT_PACK_BENCHMARK_PROTO_HDRS2
data_def.proto
OPTION "namespace=struct_pb_sample"
)
target_sources(struct_pack_benchmark PRIVATE
${STRUCT_PACK_BENCHMARK_PROTO_SRCS2}
${STRUCT_PACK_BENCHMARK_PROTO_HDRS2}
)
target_compile_definitions(struct_pack_benchmark PRIVATE HAVE_STRUCT_PB)
endif()
target_compile_definitions(struct_pack_benchmark PRIVATE HAVE_PROTOBUF)
endif () endif ()
target_compile_definitions(struct_pack_benchmark PRIVATE MSGPACK_NO_BOOST) target_compile_definitions(struct_pack_benchmark PRIVATE MSGPACK_NO_BOOST)

View File

@ -1,7 +1,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#define SEQUENTIAL_PARSE
#include "struct_pack_sample.hpp" #include "struct_pack_sample.hpp"
#if __has_include(<msgpack.hpp>) #if __has_include(<msgpack.hpp>)
@ -14,10 +14,8 @@
#ifdef HAVE_PROTOBUF #ifdef HAVE_PROTOBUF
#include "protobuf_sample.hpp" #include "protobuf_sample.hpp"
#ifdef HAVE_STRUCT_PB #endif
#include "struct_pb_sample.hpp" #include "struct_pb_sample.hpp"
#endif
#endif
#ifdef HAVE_FLATBUFFER #ifdef HAVE_FLATBUFFER
#include "flatbuffer_sample.hpp" #include "flatbuffer_sample.hpp"
#endif #endif
@ -120,11 +118,9 @@ int main(int argc, char** argv) {
map.emplace(LibType::MSGPACK, new message_pack_sample()); map.emplace(LibType::MSGPACK, new message_pack_sample());
#endif #endif
#ifdef HAVE_PROTOBUF #ifdef HAVE_PROTOBUF
#ifdef HAVE_STRUCT_PB
map.emplace(LibType::STRUCT_PB, new struct_pb_sample::struct_pb_sample_t());
#endif
map.emplace(LibType::PROTOBUF, new protobuf_sample_t()); map.emplace(LibType::PROTOBUF, new protobuf_sample_t());
#endif #endif
map.emplace(LibType::STRUCT_PB, new struct_pb_sample());
#ifdef HAVE_FLATBUFFER #ifdef HAVE_FLATBUFFER
map.emplace(LibType::FLATBUFFER, new flatbuffer_sample_t()); map.emplace(LibType::FLATBUFFER, new flatbuffer_sample_t());
#endif #endif
@ -143,9 +139,7 @@ int main(int argc, char** argv) {
run_benchmark(map, LibType::STRUCT_PACK); run_benchmark(map, LibType::STRUCT_PACK);
#ifdef HAVE_STRUCT_PB
run_benchmark(map, LibType::STRUCT_PB); run_benchmark(map, LibType::STRUCT_PB);
#endif
return 0; return 0;
} }

View File

@ -211,8 +211,6 @@ struct protobuf_sample_t : public base_sample {
buffer_.clear(); buffer_.clear();
sample.SerializeToString(&buffer_); sample.SerializeToString(&buffer_);
T obj;
uint64_t ns = 0; uint64_t ns = 0;
std::string bench_name = std::string bench_name =
name() + " deserialize " + get_sample_name(sample_type); name() + " deserialize " + get_sample_name(sample_type);
@ -220,6 +218,7 @@ struct protobuf_sample_t : public base_sample {
{ {
ScopedTimer timer(bench_name.data(), ns); ScopedTimer timer(bench_name.data(), ns);
for (int i = 0; i < ITERATIONS; ++i) { for (int i = 0; i < ITERATIONS; ++i) {
T obj;
obj.ParseFromString(buffer_); obj.ParseFromString(buffer_);
no_op((char *)&obj); no_op((char *)&obj);
no_op(buffer_); no_op(buffer_);

View File

@ -148,8 +148,6 @@ struct struct_pack_sample : public base_sample {
buffer_.clear(); buffer_.clear();
struct_pack::serialize_to(buffer_, sample); struct_pack::serialize_to(buffer_, sample);
U obj;
uint64_t ns = 0; uint64_t ns = 0;
std::string bench_name = std::string bench_name =
name() + " deserialize " + get_sample_name(sample_type); name() + " deserialize " + get_sample_name(sample_type);
@ -157,6 +155,7 @@ struct struct_pack_sample : public base_sample {
{ {
ScopedTimer timer(bench_name.data(), ns); ScopedTimer timer(bench_name.data(), ns);
for (int i = 0; i < ITERATIONS; ++i) { for (int i = 0; i < ITERATIONS; ++i) {
U obj;
[[maybe_unused]] auto ec = struct_pack::deserialize_to(obj, buffer_); [[maybe_unused]] auto ec = struct_pack::deserialize_to(obj, buffer_);
no_op((char *)&obj); no_op((char *)&obj);
no_op(buffer_); no_op(buffer_);

View File

@ -1,162 +1,208 @@
#pragma once #pragma once
#include <cassert> #include <ylt/struct_pb.hpp>
#include <optional>
#include <stdexcept>
#include <valarray>
#include "ScopedTimer.hpp" #include "ScopedTimer.hpp"
#include "no_op.h" #include "no_op.h"
#include "sample.hpp" #include "sample.hpp"
#ifdef HAVE_PROTOBUF namespace pb_sample {
#include "data_def.struct_pb.h" struct person : public iguana::base_impl<person> {
#endif person() = default;
person(int32_t a, std::string b, int c, double d)
: id(a), name(std::move(b)), age(c), salary(d) {}
int32_t id;
std::string name;
int age;
double salary;
};
REFLECTION(person, id, name, age, salary);
namespace struct_pb_sample { struct persons : public iguana::base_impl<persons> {
persons() = default;
explicit persons(std::vector<person> l) : list(std::move(l)) {}
std::vector<person> list;
};
REFLECTION(persons, list);
inline auto create_rects(std::size_t object_count) { struct rect : public iguana::base_impl<rect> {
rect32s rcs; rect() = default;
for (int i = 0; i < object_count; ++i) { rect(int32_t a, int32_t b, int32_t c, int32_t d)
rect32 rc{1, 0, 11, 1}; : x(a), y(b), width(c), height(d) {}
rcs.rect32_list.emplace_back(rc); int32_t x = 1;
int32_t y = 0;
int32_t width = 11;
int32_t height = 1;
};
REFLECTION(rect, x, y, width, height);
struct rects : public iguana::base_impl<rects> {
rects() = default;
explicit rects(std::vector<rect> l) : list(std::move(l)) {}
std::vector<rect> list;
};
REFLECTION(rects, list);
struct Vec3 : public iguana::base_impl<Vec3> {
Vec3() = default;
Vec3(float a, float b, float c) : x(a), y(b), z(c) {}
float x;
float y;
float z;
REFLECTION(Vec3, x, y, z);
};
struct Weapon : public iguana::base_impl<Weapon> {
Weapon() = default;
Weapon(std::string s, int32_t d) : name(std::move(s)), damage(d) {}
std::string name;
int32_t damage;
};
REFLECTION(Weapon, name, damage);
enum Color : uint8_t { Red, Green, Blue };
struct Monster : public iguana::base_impl<Monster> {
Monster() = default;
Monster(Vec3 a, int32_t b, int32_t c, std::string d, std::string e, Color f,
std::vector<Weapon> g, Weapon h, std::vector<Vec3> i) {}
Vec3 pos;
int32_t mana;
int32_t hp;
std::string name;
std::string inventory;
Color color;
std::vector<Weapon> weapons;
Weapon equipped;
std::vector<Vec3> path;
};
REFLECTION(Monster, pos, mana, hp, name, inventory, color, weapons, equipped,
path);
struct Monsters : public iguana::base_impl<Monsters> {
Monsters() = default;
explicit Monsters(std::vector<Monster> l) : list(std::move(l)) {}
std::vector<Monster> list;
};
REFLECTION(Monsters, list);
inline auto create_rects(size_t object_count) {
rect rc{1, 0, 11, 1};
std::vector<rect> v{};
for (std::size_t i = 0; i < object_count; i++) {
v.push_back(rc);
} }
return rcs; return v;
} }
inline auto create_persons(std::size_t object_count) {
persons ps; inline auto create_persons(size_t object_count) {
for (int i = 0; i < object_count; ++i) { std::vector<person> v{};
person p{.id = 432798, person p{432798, std::string(1024, 'A'), 24, 65536.42};
.name = std::string(1024, 'A'),
.age = 24, for (std::size_t i = 0; i < object_count; i++) {
.salary = 65536.42}; v.push_back(p);
ps.person_list.emplace_back(p);
} }
return ps; return v;
} }
inline auto create_monsters(std::size_t object_count) {
Monsters monsters; inline std::vector<Monster> create_monsters(size_t object_count) {
for (int i = 0; i < object_count / 2; ++i) { std::vector<Monster> v{};
{ Monster m = {
Monster m{}; Vec3{1, 2, 3},
m.pos = std::make_unique<struct_pb_sample::Vec3>(); 16,
*m.pos = struct_pb_sample::Vec3{1, 2, 3}; 24,
// m.pos = {1, 2, 3}; "it is a test",
m.mana = 16; "\1\2\3\4",
m.hp = 24; Color::Red,
m.name = "it is a test"; {{"gun", 42}, {"shotgun", 56}},
m.inventory = "\1\2\3\4"; {"air craft", 67},
m.color = Monster::Color::Red; {{7, 8, 9}, {71, 81, 91}},
m.weapons = {{"gun", 42}, {"shotgun", 56}}; };
m.equipped = std::make_unique<struct_pb_sample::Weapon>();
*m.equipped = struct_pb_sample::Weapon{"air craft", 67}; Monster m1 = {
// m.equipped = {"air craft", 67}; {11, 22, 33},
m.path = {{7, 8, 9}, {71, 81, 91}}; 161,
monsters.monsters.push_back(std::move(m)); 241,
} "it is a test, ok",
{ "\24\25\26\24",
Monster m; Color::Red,
m.pos = std::make_unique<struct_pb_sample::Vec3>(); {{"gun", 421}, {"shotgun", 561}},
*m.pos = struct_pb_sample::Vec3{11, 22, 33}; {"air craft", 671},
m.mana = 161; {{71, 82, 93}, {711, 821, 931}},
m.hp = 241; };
m.name = "it is a test, ok";
m.inventory = "\24\25\26\24"; for (std::size_t i = 0; i < object_count / 2; i++) {
m.color = Monster::Color::Red; v.push_back(m);
m.weapons = {{"gun", 421}, {"shotgun", 561}}; v.push_back(m1);
m.equipped = std::make_unique<struct_pb_sample::Weapon>();
*m.equipped = struct_pb_sample::Weapon{"air craft", 671};
// m.equipped = {"air craft", 671};
m.path = {{71, 82, 93}, {711, 821, 931}};
monsters.monsters.push_back(std::move(m));
}
} }
if (object_count % 2 == 1) { if (object_count % 2 == 1) {
Monster m{}; v.push_back(m);
m.pos = std::make_unique<struct_pb_sample::Vec3>();
*m.pos = struct_pb_sample::Vec3{1, 2, 3};
// m.pos = {1, 2, 3};
m.mana = 16;
m.hp = 24;
m.name = "it is a test";
m.inventory = "\1\2\3\4";
m.color = Monster::Color::Red;
m.weapons = {{"gun", 42}, {"shotgun", 56}};
m.equipped = std::make_unique<struct_pb_sample::Weapon>();
*m.equipped = struct_pb_sample::Weapon{"air craft", 67};
// m.equipped = {"air craft", 67};
m.path = {{7, 8, 9}, {71, 81, 91}};
monsters.monsters.push_back(std::move(m));
} }
return monsters;
}
struct struct_pb_sample_t : public base_sample { return v;
}
} // namespace pb_sample
struct struct_pb_sample : public base_sample {
static inline constexpr LibType lib_type = LibType::STRUCT_PB; static inline constexpr LibType lib_type = LibType::STRUCT_PB;
std::string name() const override { return get_lib_name(lib_type); } std::string name() const override { return get_lib_name(lib_type); }
void create_samples() override { void create_samples() override {
rects_ = struct_pb_sample::create_rects(OBJECT_COUNT); rects_.list = {pb_sample::create_rects(OBJECT_COUNT)};
persons_ = struct_pb_sample::create_persons(OBJECT_COUNT); persons_.list = {pb_sample::create_persons(OBJECT_COUNT)};
monsters_ = struct_pb_sample::create_monsters(OBJECT_COUNT); monsters_.list = {pb_sample::create_monsters(OBJECT_COUNT)};
} }
void do_serialization() override { void do_serialization() override {
serialize(SampleType::RECT, rects_.rect32_list[0]); serialize(SampleType::RECT, rects_.list[0]);
struct_pb::to_pb(rects_, buffer_);
struct_pb::to_pb(rects_, buffer_);
struct_pb::to_pb(rects_, buffer_);
std::cout << rects_.list.size() << "\n";
serialize(SampleType::RECTS, rects_); serialize(SampleType::RECTS, rects_);
serialize(SampleType::PERSON, persons_.person_list[0]); serialize(SampleType::PERSON, persons_.list[0]);
serialize(SampleType::PERSONS, persons_); serialize(SampleType::PERSONS, persons_);
serialize(SampleType::MONSTER, monsters_.monsters[0]); serialize(SampleType::MONSTER, monsters_.list[0]);
serialize(SampleType::MONSTERS, monsters_); serialize(SampleType::MONSTERS, monsters_);
} }
void do_deserialization() override { void do_deserialization() override {
deserialize(SampleType::RECT, rects_.rect32_list[0]); deserialize(SampleType::RECT, rects_.list[0]);
deserialize(SampleType::RECTS, rects_); deserialize(SampleType::RECTS, rects_);
deserialize(SampleType::PERSON, persons_.person_list[0]); deserialize(SampleType::PERSON, persons_.list[0]);
deserialize(SampleType::PERSONS, persons_); deserialize(SampleType::PERSONS, persons_);
deserialize(SampleType::MONSTER, monsters_.monsters[0]); deserialize(SampleType::MONSTER, monsters_.list[0]);
deserialize(SampleType::MONSTERS, monsters_); deserialize(SampleType::MONSTERS, monsters_);
} }
private: private:
template <typename T> template <typename T>
void serialize(SampleType sample_type, T& sample) { void serialize(SampleType sample_type, T &sample) {
auto sz = struct_pb::internal::get_needed_size(sample);
buffer_.resize(sz);
struct_pb::internal::serialize_to(buffer_.data(), buffer_.size(), sample);
buffer_.clear();
buffer_.resize(sz);
uint64_t ns = 0;
std::string bench_name =
name() + " serialize " + get_sample_name(sample_type);
{ {
ScopedTimer timer(bench_name.data(), ns); struct_pb::to_pb(sample, buffer_);
for (int i = 0; i < ITERATIONS; ++i) { buffer_.clear();
buffer_.clear();
auto sz = struct_pb::internal::get_needed_size(sample);
buffer_.resize(sz);
struct_pb::internal::serialize_to(buffer_.data(), buffer_.size(),
sample);
no_op(buffer_);
no_op((char*)&sample);
}
}
ser_time_elapsed_map_.emplace(sample_type, ns); uint64_t ns = 0;
std::string bench_name =
name() + " serialize " + get_sample_name(sample_type);
{
ScopedTimer timer(bench_name.data(), ns);
for (int i = 0; i < ITERATIONS; ++i) {
struct_pb::to_pb(sample, buffer_);
no_op(buffer_);
no_op((char *)&sample);
}
}
ser_time_elapsed_map_.emplace(sample_type, ns);
}
buf_size_map_.emplace(sample_type, buffer_.size()); buf_size_map_.emplace(sample_type, buffer_.size());
} }
template <typename T, typename U = T>
template <typename T> void deserialize(SampleType sample_type, T &sample) {
void deserialize(SampleType sample_type, T& sample) {
buffer_.clear(); buffer_.clear();
auto sz = struct_pb::internal::get_needed_size(sample); struct_pb::to_pb(sample, buffer_);
buffer_.resize(sz);
struct_pb::internal::serialize_to(buffer_.data(), buffer_.size(), sample);
T obj;
// vec.resize(ITERATIONS);
uint64_t ns = 0; uint64_t ns = 0;
std::string bench_name = std::string bench_name =
@ -165,21 +211,17 @@ struct struct_pb_sample_t : public base_sample {
{ {
ScopedTimer timer(bench_name.data(), ns); ScopedTimer timer(bench_name.data(), ns);
for (int i = 0; i < ITERATIONS; ++i) { for (int i = 0; i < ITERATIONS; ++i) {
[[maybe_unused]] auto ok = struct_pb::internal::deserialize_to( U obj;
obj, buffer_.data(), buffer_.size()); struct_pb::from_pb(obj, buffer_);
assert(ok); no_op((char *)&obj);
no_op((char*)&obj);
no_op(buffer_); no_op(buffer_);
} }
} }
deser_time_elapsed_map_.emplace(sample_type, ns); deser_time_elapsed_map_.emplace(sample_type, ns);
buf_size_map_.emplace(sample_type, buffer_.size());
} }
struct_pb_sample::rect32s rects_; pb_sample::rects rects_;
struct_pb_sample::persons persons_; pb_sample::persons persons_;
struct_pb_sample::Monsters monsters_; pb_sample::Monsters monsters_;
std::string buffer_; std::string buffer_;
}; };
} // namespace struct_pb_sample

View File

@ -1,2 +0,0 @@
add_subdirectory(protoc-plugin)
add_subdirectory(conformance)

View File

@ -1,68 +0,0 @@
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests/struct_pb)
find_package(Protobuf QUIET)
if (NOT Protobuf_FOUND)
message(INFO "to build struct_pb conformance test, you must install libprotoc first")
return()
endif ()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(conformance_test_runner
binary_json_conformance_suite.cc
binary_json_conformance_suite.h
conformance_test_runner.cc
conformance_test_main.cc
conformance_test.cc
conformance_test.h
)
target_sources(conformance_test_runner PRIVATE
conformance/conformance.proto
google/protobuf/test_messages_proto2.proto
google/protobuf/test_messages_proto3.proto
)
protobuf_generate(TARGET conformance_test_runner)
target_include_directories(conformance_test_runner PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(conformance_test_runner PRIVATE protobuf::libprotobuf)
add_executable(conformance_cpp conformance_cpp.cc)
target_link_libraries(conformance_cpp PRIVATE protobuf::libprotobuf)
target_sources(conformance_cpp PRIVATE
conformance/conformance.proto
google/protobuf/test_messages_proto2.proto
google/protobuf/test_messages_proto3.proto
)
protobuf_generate(TARGET conformance_cpp)
target_include_directories(conformance_cpp PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
add_test(NAME conformance_cpp_test
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/conformance_test_runner
--failure_list ${CMAKE_CURRENT_SOURCE_DIR}/failure_list_cpp.txt
--output_dir ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/conformance_cpp
DEPENDS conformance_test_runner conformance_cpp)
add_executable(conformance_struct_pb
conformance_struct_pb.cc
)
protobuf_generate_struct_pb(SRCS HDRS
conformance/conformance.proto
google/protobuf/test_messages_proto2.proto
google/protobuf/test_messages_proto3.proto
google/protobuf/any.proto
google/protobuf/descriptor.proto
google/protobuf/duration.proto
google/protobuf/field_mask.proto
google/protobuf/struct.proto
google/protobuf/timestamp.proto
google/protobuf/type.proto
google/protobuf/wrappers.proto
google/protobuf/source_context.proto
TARGET conformance_struct_pb
)
target_sources(conformance_struct_pb PRIVATE ${SRCS} ${HDRS})
target_include_directories(conformance_struct_pb PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
add_test(NAME conformance_struct_pb_test
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/conformance_test_runner
--failure_list ${CMAKE_CURRENT_SOURCE_DIR}/failure_list_cpp.txt
--output_dir ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/conformance_struct_pb
DEPENDS conformance_test_runner conformance_struct_pb)

View File

@ -1,208 +0,0 @@
#pragma once
// absl compat
#include <iostream>
#include <string_view>
#include "absl/strings/str_cat.h"
// #include "google/protobuf/stubs/status.h"
namespace absl {
// using namespace google::protobuf::util;
enum class StatusCode : int {
kOk = 0,
kCancelled = 1,
kUnknown = 2,
kInvalidArgument = 3,
kDeadlineExceeded = 4,
kNotFound = 5,
kAlreadyExists = 6,
kPermissionDenied = 7,
kResourceExhausted = 8,
kFailedPrecondition = 9,
kAborted = 10,
kOutOfRange = 11,
kUnimplemented = 12,
kInternal = 13,
kUnavailable = 14,
kDataLoss = 15,
kUnauthenticated = 16,
};
class Status {
public:
Status(StatusCode code = StatusCode::kOk, std::string_view message = {})
: code_(code), msg_(message) {}
bool ok() const { return code_ == StatusCode::kOk; }
std::string message() const {
// fix gcc 10
return std::string(msg_);
}
private:
StatusCode code_;
std::string_view msg_;
friend std::ostream& operator<<(std::ostream& os, const Status& x);
};
inline std::ostream& operator<<(std::ostream& os, const Status& x) {
os << static_cast<int>(x.code_) << " " << x.msg_;
return os;
}
Status DataLossError(std::string_view message) {
return Status(absl::StatusCode::kDataLoss, message);
}
StatusCode ErrnoToStatusCode(int error_number) {
switch (error_number) {
case 0:
return StatusCode::kOk;
case EINVAL: // Invalid argument
case ENAMETOOLONG: // Filename too long
case E2BIG: // Argument list too long
case EDESTADDRREQ: // Destination address required
case EDOM: // Mathematics argument out of domain of function
case EFAULT: // Bad address
case EILSEQ: // Illegal byte sequence
case ENOPROTOOPT: // Protocol not available
case ENOSTR: // Not a STREAM
case ENOTSOCK: // Not a socket
case ENOTTY: // Inappropriate I/O control operation
case EPROTOTYPE: // Protocol wrong type for socket
case ESPIPE: // Invalid seek
return StatusCode::kInvalidArgument;
case ETIMEDOUT: // Connection timed out
case ETIME: // Timer expired
return StatusCode::kDeadlineExceeded;
case ENODEV: // No such device
case ENOENT: // No such file or directory
#ifdef ENOMEDIUM
case ENOMEDIUM: // No medium found
#endif
case ENXIO: // No such device or address
case ESRCH: // No such process
return StatusCode::kNotFound;
case EEXIST: // File exists
case EADDRNOTAVAIL: // Address not available
case EALREADY: // Connection already in progress
#ifdef ENOTUNIQ
case ENOTUNIQ: // Name not unique on network
#endif
return StatusCode::kAlreadyExists;
case EPERM: // Operation not permitted
case EACCES: // Permission denied
#ifdef ENOKEY
case ENOKEY: // Required key not available
#endif
case EROFS: // Read only file system
return StatusCode::kPermissionDenied;
case ENOTEMPTY: // Directory not empty
case EISDIR: // Is a directory
case ENOTDIR: // Not a directory
case EADDRINUSE: // Address already in use
case EBADF: // Invalid file descriptor
#ifdef EBADFD
case EBADFD: // File descriptor in bad state
#endif
case EBUSY: // Device or resource busy
case ECHILD: // No child processes
case EISCONN: // Socket is connected
#ifdef EISNAM
case EISNAM: // Is a named type file
#endif
#ifdef ENOTBLK
case ENOTBLK: // Block device required
#endif
case ENOTCONN: // The socket is not connected
case EPIPE: // Broken pipe
#ifdef ESHUTDOWN
case ESHUTDOWN: // Cannot send after transport endpoint shutdown
#endif
case ETXTBSY: // Text file busy
#ifdef EUNATCH
case EUNATCH: // Protocol driver not attached
#endif
return StatusCode::kFailedPrecondition;
case ENOSPC: // No space left on device
#ifdef EDQUOT
case EDQUOT: // Disk quota exceeded
#endif
case EMFILE: // Too many open files
case EMLINK: // Too many links
case ENFILE: // Too many open files in system
case ENOBUFS: // No buffer space available
case ENODATA: // No message is available on the STREAM read queue
case ENOMEM: // Not enough space
case ENOSR: // No STREAM resources
#ifdef EUSERS
case EUSERS: // Too many users
#endif
return StatusCode::kResourceExhausted;
#ifdef ECHRNG
case ECHRNG: // Channel number out of range
#endif
case EFBIG: // File too large
case EOVERFLOW: // Value too large to be stored in data type
case ERANGE: // Result too large
return StatusCode::kOutOfRange;
#ifdef ENOPKG
case ENOPKG: // Package not installed
#endif
case ENOSYS: // Function not implemented
case ENOTSUP: // Operation not supported
case EAFNOSUPPORT: // Address family not supported
#ifdef EPFNOSUPPORT
case EPFNOSUPPORT: // Protocol family not supported
#endif
case EPROTONOSUPPORT: // Protocol not supported
#ifdef ESOCKTNOSUPPORT
case ESOCKTNOSUPPORT: // Socket type not supported
#endif
case EXDEV: // Improper link
return StatusCode::kUnimplemented;
case EAGAIN: // Resource temporarily unavailable
#ifdef ECOMM
case ECOMM: // Communication error on send
#endif
case ECONNREFUSED: // Connection refused
case ECONNABORTED: // Connection aborted
case ECONNRESET: // Connection reset
case EINTR: // Interrupted function call
#ifdef EHOSTDOWN
case EHOSTDOWN: // Host is down
#endif
case EHOSTUNREACH: // Host is unreachable
case ENETDOWN: // Network is down
case ENETRESET: // Connection aborted by network
case ENETUNREACH: // Network unreachable
case ENOLCK: // No locks available
case ENOLINK: // Link has been severed
#ifdef ENONET
case ENONET: // Machine is not on the network
#endif
return StatusCode::kUnavailable;
case EDEADLK: // Resource deadlock avoided
#ifdef ESTALE
case ESTALE: // Stale file handle
#endif
return StatusCode::kAborted;
case ECANCELED: // Operation cancelled
return StatusCode::kCancelled;
default:
return StatusCode::kUnknown;
}
}
std::string MessageForErrnoToStatus(int error_number, std::string message) {
return message + ": " + std::to_string(error_number);
}
Status ErrnoToStatus(int error_number, std::string message) {
return Status(ErrnoToStatusCode(error_number),
MessageForErrnoToStatus(error_number, message));
}
inline Status OkStatus() { return Status(); }
Status NotFoundError(std::string_view message) {
return Status(absl::StatusCode::kNotFound, message);
}
Status InvalidArgumentError(std::string_view message) {
return Status(absl::StatusCode::kInvalidArgument, message);
}
Status UnimplementedError(std::string_view message) {
return Status(absl::StatusCode::kUnimplemented, message);
}
} // namespace absl

View File

@ -1,20 +0,0 @@
#pragma once
// absl compat
#include "status.h"
namespace absl {
template <typename T>
class StatusOr {
public:
StatusOr(Status status) : status_(status) {}
StatusOr(const T& data) : data_(data) {}
Status status() const { return status_; }
T* operator->() { return &data_; }
bool ok() const { return status_.ok(); }
const T& operator*() const& { return data_; }
private:
Status status_;
T data_;
};
} // namespace absl

View File

@ -1,29 +0,0 @@
#pragma
#include <string>
// #include "google/protobuf/stubs/strutil.h"
// compat for absl::StrCat
namespace absl {
// using google::protobuf::StrCat;
template <typename T>
std::string StrCat(const T& t) {
if constexpr (std::same_as<T, std::string>) {
return t;
}
else if constexpr (std::integral<T>) {
return std::to_string(t);
}
else if constexpr (std::is_enum_v<T>) {
int n = static_cast<int>(t);
return std::to_string(n);
}
else {
return std::string(t);
}
}
template <typename T, typename... Args>
std::string StrCat(const T& t, const Args&... args) {
return StrCat(t) + StrCat(args...);
}
} // namespace absl

View File

@ -1,92 +0,0 @@
#pragma once
#include <string>
// compat only
namespace absl {
template <typename T>
auto to(const T& t) {
if constexpr (std::same_as<T, std::string>) {
return t.c_str();
}
else {
return t;
}
}
class SimpleAppender {
public:
SimpleAppender(std::string& buffer, std::string_view format)
: buffer(buffer), format_(format) {}
void format() {
buffer += format_.substr(index);
index = format_.size();
}
template <typename T, typename... Args>
void format(const T& t, const Args&... args) {
while (index < format_.size()) {
if (format_[index] != '%') {
buffer += format_[index];
index++;
continue;
}
if (index + 1 < format_.size()) {
if (format_[index + 1] == 's') {
if constexpr (std::same_as<std::string, T>) {
buffer += t;
}
else if constexpr (std::same_as<std::string_view, T>) {
buffer += t;
}
else if constexpr (std::same_as<char*, std::decay_t<T>>) {
buffer += std::string(t);
}
else {
buffer += "'ERROR FORMAT'";
}
index++;
index++;
format(args...);
}
else if (format_[index + 1] == 'd') {
if constexpr (std::integral<T>) {
buffer += std::to_string(t);
}
else {
buffer += "'ERROR FORMAT'";
}
index++;
index++;
format(args...);
}
else if (format_[index + 1] == 'z') {
if (index + 2 < format_.size() && format_[index + 2] == 'u') {
if constexpr (std::integral<T>) {
buffer += std::to_string(t);
}
else {
buffer += "'ERROR FORMAT'";
}
index++;
index++;
index++;
format(args...);
}
}
}
else {
buffer += "'maybe ERROR FORMAT'";
break;
}
}
}
private:
std::string& buffer;
std::string_view format_;
int index = 0;
};
template <typename... Args>
void StrAppendFormat(std::string* output, const char* format,
const Args&... args) {
SimpleAppender(*output, format).format(args...);
// std::cout << "result: " << *output << std::endl;
}
} // namespace absl

File diff suppressed because it is too large Load Diff

View File

@ -1,123 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CONFORMANCE_BINARY_JSON_CONFORMANCE_SUITE_H
#define CONFORMANCE_BINARY_JSON_CONFORMANCE_SUITE_H
#include "conformance_test.h"
#include "google/protobuf/descriptor.h"
namespace google {
namespace protobuf {
class BinaryAndJsonConformanceSuite : public ConformanceTestSuite {
public:
BinaryAndJsonConformanceSuite() {}
private:
void RunSuiteImpl() override;
void RunBinaryPerformanceTests();
void RunValidJsonTest(const std::string& test_name, ConformanceLevel level,
const std::string& input_json,
const std::string& equivalent_text_format,
bool is_proto3);
void RunValidProtobufTest(const std::string& test_name,
ConformanceLevel level,
const std::string& input_protobuf,
const std::string& equivalent_text_format,
bool is_proto3);
void RunValidBinaryProtobufTest(const std::string& test_name,
ConformanceLevel level,
const std::string& input_protobuf,
bool is_proto3);
void RunValidBinaryProtobufTest(const std::string& test_name,
ConformanceLevel level,
const std::string& input_protobuf,
const std::string& expected_protobuf,
bool is_proto3);
void RunBinaryPerformanceMergeMessageWithField(const std::string& test_name,
const std::string& field_proto,
bool is_proto3);
void RunValidProtobufTestWithMessage(
const std::string& test_name, ConformanceLevel level,
const Message* input, const std::string& equivalent_text_format,
bool is_proto3);
bool ParseResponse(const conformance::ConformanceResponse& response,
const ConformanceRequestSetting& setting,
Message* test_message) override;
void ExpectParseFailureForJson(const std::string& test_name,
ConformanceLevel level,
const std::string& input_json);
void ExpectSerializeFailureForJson(const std::string& test_name,
ConformanceLevel level,
const std::string& text_format);
void ExpectParseFailureForProtoWithProtoVersion(const std::string& proto,
const std::string& test_name,
ConformanceLevel level,
bool is_proto3);
void ExpectParseFailureForProto(const std::string& proto,
const std::string& test_name,
ConformanceLevel level);
void ExpectHardParseFailureForProto(const std::string& proto,
const std::string& test_name,
ConformanceLevel level);
void TestPrematureEOFForType(google::protobuf::FieldDescriptor::Type type);
void TestIllegalTags();
template <class MessageType>
void TestOneofMessage(MessageType& message, bool is_proto3);
template <class MessageType>
void TestUnknownMessage(MessageType& message, bool is_proto3);
void TestValidDataForType(
google::protobuf::FieldDescriptor::Type,
std::vector<std::pair<std::string, std::string>> values);
void TestValidDataForRepeatedScalarMessage();
void TestValidDataForMapType(google::protobuf::FieldDescriptor::Type,
google::protobuf::FieldDescriptor::Type);
void TestValidDataForOneofType(google::protobuf::FieldDescriptor::Type);
void TestMergeOneofMessage();
void TestOverwriteMessageValueMap();
void TestBinaryPerformanceForAlternatingUnknownFields();
void TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
google::protobuf::FieldDescriptor::Type);
void TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
google::protobuf::FieldDescriptor::Type);
void TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
google::protobuf::FieldDescriptor::Type, std::string field_value);
std::unique_ptr<google::protobuf::util::TypeResolver> type_resolver_;
std::string type_url_;
};
} // namespace protobuf
} // namespace google
#endif // CONFORMANCE_BINARY_JSON_CONFORMANCE_SUITE_H

View File

@ -1,180 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package conformance;
option java_package = "com.google.protobuf.conformance";
option objc_class_prefix = "Conformance";
// This defines the conformance testing protocol. This protocol exists between
// the conformance test suite itself and the code being tested. For each test,
// the suite will send a ConformanceRequest message and expect a
// ConformanceResponse message.
//
// You can either run the tests in two different ways:
//
// 1. in-process (using the interface in conformance_test.h).
//
// 2. as a sub-process communicating over a pipe. Information about how to
// do this is in conformance_test_runner.cc.
//
// Pros/cons of the two approaches:
//
// - running as a sub-process is much simpler for languages other than C/C++.
//
// - running as a sub-process may be more tricky in unusual environments like
// iOS apps, where fork/stdin/stdout are not available.
enum WireFormat {
UNSPECIFIED = 0;
PROTOBUF = 1;
JSON = 2;
JSPB = 3; // Only used inside Google. Opensource testees just skip it.
TEXT_FORMAT = 4;
}
enum TestCategory {
UNSPECIFIED_TEST = 0;
BINARY_TEST = 1; // Test binary wire format.
JSON_TEST = 2; // Test json wire format.
// Similar to JSON_TEST. However, during parsing json, testee should ignore
// unknown fields. This feature is optional. Each implementation can decide
// whether to support it. See
// https://developers.google.com/protocol-buffers/docs/proto3#json_options
// for more detail.
JSON_IGNORE_UNKNOWN_PARSING_TEST = 3;
// Test jspb wire format. Only used inside Google. Opensource testees just
// skip it.
JSPB_TEST = 4;
// Test text format. For cpp, java and python, testees can already deal with
// this type. Testees of other languages can simply skip it.
TEXT_FORMAT_TEST = 5;
}
// The conformance runner will request a list of failures as the first request.
// This will be known by message_type == "conformance.FailureSet", a conformance
// test should return a serialized FailureSet in protobuf_payload.
message FailureSet {
repeated string failure = 1;
}
// Represents a single test case's input. The testee should:
//
// 1. parse this proto (which should always succeed)
// 2. parse the protobuf or JSON payload in "payload" (which may fail)
// 3. if the parse succeeded, serialize the message in the requested format.
message ConformanceRequest {
// The payload (whether protobuf of JSON) is always for a
// protobuf_test_messages.proto3.TestAllTypes proto (as defined in
// src/google/protobuf/proto3_test_messages.proto).
oneof payload {
bytes protobuf_payload = 1;
string json_payload = 2;
// Only used inside Google. Opensource testees just skip it.
string jspb_payload = 7;
string text_payload = 8;
}
// Which format should the testee serialize its message to?
WireFormat requested_output_format = 3;
// The full name for the test message to use; for the moment, either:
// protobuf_test_messages.proto3.TestAllTypesProto3 or
// protobuf_test_messages.google.protobuf.TestAllTypesProto2.
string message_type = 4;
// Each test is given a specific test category. Some category may need
// specific support in testee programs. Refer to the definition of
// TestCategory for more information.
TestCategory test_category = 5;
// Specify details for how to encode jspb.
JspbEncodingConfig jspb_encoding_options = 6;
// This can be used in json and text format. If true, testee should print
// unknown fields instead of ignore. This feature is optional.
bool print_unknown_fields = 9;
}
// Represents a single test case's output.
message ConformanceResponse {
oneof result {
// This string should be set to indicate parsing failed. The string can
// provide more information about the parse error if it is available.
//
// Setting this string does not necessarily mean the testee failed the
// test. Some of the test cases are intentionally invalid input.
string parse_error = 1;
// If the input was successfully parsed but errors occurred when
// serializing it to the requested output format, set the error message in
// this field.
string serialize_error = 6;
// This should be set if the test program timed out. The string should
// provide more information about what the child process was doing when it
// was killed.
string timeout_error = 9;
// This should be set if some other error occurred. This will always
// indicate that the test failed. The string can provide more information
// about the failure.
string runtime_error = 2;
// If the input was successfully parsed and the requested output was
// protobuf, serialize it to protobuf and set it in this field.
bytes protobuf_payload = 3;
// If the input was successfully parsed and the requested output was JSON,
// serialize to JSON and set it in this field.
string json_payload = 4;
// For when the testee skipped the test, likely because a certain feature
// wasn't supported, like JSON input/output.
string skipped = 5;
// If the input was successfully parsed and the requested output was JSPB,
// serialize to JSPB and set it in this field. JSPB is only used inside
// Google. Opensource testees can just skip it.
string jspb_payload = 7;
// If the input was successfully parsed and the requested output was
// TEXT_FORMAT, serialize to TEXT_FORMAT and set it in this field.
string text_payload = 8;
}
}
// Encoding options for jspb format.
message JspbEncodingConfig {
// Encode the value field of Any as jspb array if true, otherwise binary.
bool use_jspb_array_any_format = 1;
}

View File

@ -1,304 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "conformance/conformance.pb.h"
#include "google/protobuf/message.h"
#include "google/protobuf/stubs/logging.h"
#include "google/protobuf/test_messages_proto2.pb.h"
#include "google/protobuf/test_messages_proto3.pb.h"
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/json_util.h"
#include "google/protobuf/util/type_resolver.h"
#include "google/protobuf/util/type_resolver_util.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
#define RETURN_IF_ERROR(expr) \
do { \
/* Using _status below to avoid capture problems if expr is "status". */ \
const absl::Status _status = (expr); \
if (PROTOBUF_PREDICT_FALSE(!_status.ok())) \
return _status; \
} while (0)
namespace google {
namespace protobuf {
namespace {
using ::conformance::ConformanceRequest;
using ::conformance::ConformanceResponse;
using ::google::protobuf::util::BinaryToJsonString;
using ::google::protobuf::util::JsonParseOptions;
using ::google::protobuf::util::JsonToBinaryString;
using ::google::protobuf::util::NewTypeResolverForDescriptorPool;
using ::google::protobuf::util::TypeResolver;
using ::protobuf_test_messages::proto2::TestAllTypesProto2;
using ::protobuf_test_messages::proto3::TestAllTypesProto3;
absl::Status ReadFd(int fd, char* buf, size_t len) {
while (len > 0) {
ssize_t bytes_read = read(fd, buf, len);
if (bytes_read == 0) {
return absl::DataLossError("unexpected EOF");
}
if (bytes_read < 0) {
return absl::ErrnoToStatus(errno, "error reading from test runner");
}
len -= bytes_read;
buf += bytes_read;
}
return absl::OkStatus();
}
absl::Status WriteFd(int fd, const void* buf, size_t len) {
if (static_cast<size_t>(write(fd, buf, len)) != len) {
return absl::ErrnoToStatus(errno, "error reading to test runner");
}
return absl::OkStatus();
}
// define this macro when you want to debug struct_pb conformance test
//
#ifdef CONFORMANCE_CPP_DUMP_DATA
static int index = 0;
void write_to_file(const std::string& buffer, const std::string& tag,
const std::string& suffix) {
std::string filename = tag + "/" + std::to_string(index) + "." + suffix;
std::ofstream ofs;
ofs.open(filename, std::ios::out | std::ios::binary);
ofs.write(buffer.data(), buffer.size());
}
#endif
class Harness {
public:
Harness() {
google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
resolver_.reset(NewTypeResolverForDescriptorPool(
"type.googleapis.com", DescriptorPool::generated_pool()));
type_url_ = absl::StrCat("type.googleapis.com/",
TestAllTypesProto3::GetDescriptor()->full_name());
}
absl::StatusOr<ConformanceResponse> RunTest(
const ConformanceRequest& request);
// Returns Ok(true) if we're done processing requests.
absl::StatusOr<bool> ServeConformanceRequest();
private:
bool verbose_ = false;
std::unique_ptr<TypeResolver> resolver_;
std::string type_url_;
};
absl::StatusOr<ConformanceResponse> Harness::RunTest(
const ConformanceRequest& request) {
const Descriptor* descriptor =
DescriptorPool::generated_pool()->FindMessageTypeByName(
request.message_type());
if (descriptor == nullptr) {
return absl::NotFoundError(
absl::StrCat("No such message type: ", request.message_type()));
}
std::unique_ptr<Message> test_message(
MessageFactory::generated_factory()->GetPrototype(descriptor)->New());
ConformanceResponse response;
switch (request.payload_case()) {
case ConformanceRequest::kProtobufPayload: {
if (!test_message->ParseFromString(request.protobuf_payload())) {
response.set_parse_error("parse error (no more details available)");
return response;
}
break;
}
case ConformanceRequest::kJsonPayload: {
JsonParseOptions options;
options.ignore_unknown_fields =
(request.test_category() ==
conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
std::string proto_binary;
auto status =
JsonToBinaryString(resolver_.get(), type_url_, request.json_payload(),
&proto_binary, options);
if (!status.ok()) {
response.set_parse_error(
absl::StrCat("parse error: ", status.message()));
return response;
}
if (!test_message->ParseFromString(proto_binary)) {
response.set_runtime_error(
"parsing JSON generated invalid proto output");
return response;
}
break;
}
case ConformanceRequest::kTextPayload: {
if (!TextFormat::ParseFromString(request.text_payload(),
test_message.get())) {
response.set_parse_error("parse error (no more details available)");
return response;
}
break;
}
case ConformanceRequest::PAYLOAD_NOT_SET:
return absl::InvalidArgumentError("request didn't have payload");
default:
return absl::InvalidArgumentError(
absl::StrCat("unknown payload type", request.payload_case()));
}
#ifdef CONFORMANCE_CPP_DUMP_DATA
write_to_file(test_message->DebugString(), "in", "msg.txt");
#endif
switch (request.requested_output_format()) {
case conformance::UNSPECIFIED:
return absl::InvalidArgumentError("unspecified output format");
case conformance::PROTOBUF: {
GOOGLE_CHECK(
test_message->SerializeToString(response.mutable_protobuf_payload()));
break;
}
case conformance::JSON: {
std::string proto_binary;
GOOGLE_CHECK(test_message->SerializeToString(&proto_binary));
auto status = BinaryToJsonString(resolver_.get(), type_url_, proto_binary,
response.mutable_json_payload());
if (!status.ok()) {
response.set_serialize_error(absl::StrCat(
"failed to serialize JSON output: ", status.message()));
}
break;
}
case conformance::TEXT_FORMAT: {
TextFormat::Printer printer;
printer.SetHideUnknownFields(!request.print_unknown_fields());
GOOGLE_CHECK(printer.PrintToString(*test_message,
response.mutable_text_payload()));
break;
}
default:
return absl::InvalidArgumentError(absl::StrCat(
"unknown output format", request.requested_output_format()));
}
return response;
}
absl::StatusOr<bool> Harness::ServeConformanceRequest() {
uint32_t in_len;
if (!ReadFd(STDIN_FILENO, reinterpret_cast<char*>(&in_len), sizeof(in_len))
.ok()) {
// EOF means we're done.
return true;
}
std::string serialized_input;
serialized_input.resize(in_len);
RETURN_IF_ERROR(ReadFd(STDIN_FILENO, &serialized_input[0], in_len));
#ifdef CONFORMANCE_CPP_DUMP_DATA
write_to_file(serialized_input, "in", "bin");
#endif
ConformanceRequest request;
GOOGLE_CHECK(request.ParseFromString(serialized_input));
#ifdef CONFORMANCE_CPP_DUMP_DATA
write_to_file(
std::to_string(serialized_input.size()) + "\n" + request.DebugString(),
"in", "txt");
#endif
absl::StatusOr<ConformanceResponse> response = RunTest(request);
RETURN_IF_ERROR(response.status());
std::string serialized_output;
response->SerializeToString(&serialized_output);
#ifdef CONFORMANCE_CPP_DUMP_DATA
write_to_file(serialized_output, "out", "bin");
write_to_file(response->DebugString(), "out", "txt");
index++;
#endif
uint32_t out_len = static_cast<uint32_t>(serialized_output.size());
RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, &out_len, sizeof(out_len)));
RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, serialized_output.data(), out_len));
if (verbose_) {
GOOGLE_LOG(INFO) << "conformance-cpp: request="
<< request.ShortDebugString()
<< ", response=" << response->ShortDebugString();
}
return false;
}
} // namespace
} // namespace protobuf
} // namespace google
int main() {
google::protobuf::Harness harness;
int total_runs = 0;
while (true) {
auto is_done = harness.ServeConformanceRequest();
if (!is_done.ok()) {
GOOGLE_LOG(FATAL) << is_done.status().message();
}
if (*is_done) {
break;
}
total_runs++;
}
GOOGLE_LOG(INFO) << "conformance-cpp: received EOF from test runner after "
<< total_runs << " tests";
}

View File

@ -1,242 +0,0 @@
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <filesystem>
#include <fstream>
#include <memory>
#include <string>
#include <utility>
namespace fs = std::filesystem;
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "conformance/conformance.struct_pb.h"
#include "google/protobuf/test_messages_proto2.struct_pb.h"
#include "google/protobuf/test_messages_proto3.struct_pb.h"
#define RETURN_IF_ERROR(expr) \
do { \
/* Using _status below to avoid capture problems if expr is "status". */ \
const absl::Status _status = (expr); \
if (!_status.ok()) \
return _status; \
} while (0)
using ::conformance::ConformanceRequest;
using ::conformance::ConformanceResponse;
using ::protobuf_test_messages::proto2::TestAllTypesProto2;
using ::protobuf_test_messages::proto3::TestAllTypesProto3;
absl::Status ReadFd(int fd, char* buf, size_t len) {
while (len > 0) {
ssize_t bytes_read = read(fd, buf, len);
if (bytes_read == 0) {
return absl::DataLossError("unexpected EOF");
}
if (bytes_read < 0) {
return absl::ErrnoToStatus(errno, "error reading from test runner");
}
len -= bytes_read;
buf += bytes_read;
}
return absl::OkStatus();
}
absl::Status WriteFd(int fd, const void* buf, size_t len) {
if (static_cast<size_t>(write(fd, buf, len)) != len) {
return absl::ErrnoToStatus(errno, "error reading to test runner");
}
return absl::OkStatus();
}
class Harness {
public:
Harness() {}
absl::StatusOr<ConformanceResponse> RunTest(
const ConformanceRequest& request);
// Returns Ok(true) if we're done processing requests.
absl::StatusOr<bool> ServeConformanceRequest();
private:
bool verbose_ = false;
std::string type_url_;
};
template <typename T>
absl::StatusOr<ConformanceResponse> run_test(
const ConformanceRequest& request) {
ConformanceResponse response{};
T data{};
struct_pb::UnknownFields unknown_fields{};
switch (request.payload_case()) {
case ConformanceRequest::PayloadCase::protobuf_payload: {
const auto& buffer = request.protobuf_payload();
bool ok = struct_pb::internal::deserialize_to(
data, buffer.data(), buffer.size(), unknown_fields);
if (!ok) {
response.set_parse_error("parse error (no more details available)");
return response;
}
break;
}
case ConformanceRequest::PayloadCase::none:
return absl::UnimplementedError("payload none");
case ConformanceRequest::PayloadCase::json_payload:
return absl::UnimplementedError("payload json");
case ConformanceRequest::PayloadCase::jspb_payload:
return absl::UnimplementedError("payload jspb");
case ConformanceRequest::PayloadCase::text_payload:
return absl::UnimplementedError("payload text");
default: {
int num = static_cast<int>(request.payload_case());
return absl::UnimplementedError("unknown payload type: " +
std::to_string(num));
}
}
switch (request.requested_output_format) {
case conformance::WireFormat::PROTOBUF: {
response.set_protobuf_payload(
struct_pb::serialize<std::string>(data, unknown_fields));
break;
}
case conformance::WireFormat::UNSPECIFIED:
return absl::InvalidArgumentError("unspecified output format");
case conformance::WireFormat::JSON:
return absl::UnimplementedError("output json");
case conformance::WireFormat::JSPB:
return absl::UnimplementedError("output jspb");
case conformance::WireFormat::TEXT_FORMAT:
return absl::UnimplementedError("output text");
default: {
int num = static_cast<int>(request.requested_output_format);
return absl::UnimplementedError("unknown output type: " +
std::to_string(num));
}
}
return response;
}
absl::StatusOr<ConformanceResponse> Harness::RunTest(
const ConformanceRequest& request) {
std::string pb2 = "protobuf_test_messages.proto2.TestAllTypesProto2";
std::string pb3 = "protobuf_test_messages.proto3.TestAllTypesProto3";
if (request.message_type == pb2) {
using Message = protobuf_test_messages::proto2::TestAllTypesProto2;
return run_test<Message>(request);
}
else if (request.message_type == pb3) {
using Message = protobuf_test_messages::proto3::TestAllTypesProto3;
return run_test<Message>(request);
}
else if (request.message_type == "conformance.FailureSet") {
return run_test<conformance::FailureSet>(request);
}
else {
return absl::NotFoundError(
absl::StrCat("No such message type: ", request.message_type));
}
}
absl::StatusOr<bool> Harness::ServeConformanceRequest() {
uint32_t in_len;
if (!ReadFd(STDIN_FILENO, reinterpret_cast<char*>(&in_len), sizeof(in_len))
.ok()) {
// EOF means we're done.
return true;
}
std::string serialized_input;
serialized_input.resize(in_len);
RETURN_IF_ERROR(ReadFd(STDIN_FILENO, &serialized_input[0], in_len));
ConformanceRequest request{};
const auto& buffer = serialized_input;
bool ok = struct_pb::internal::deserialize_to(request, buffer.data(),
buffer.size());
if (!ok) {
return false;
}
absl::StatusOr<ConformanceResponse> response = RunTest(request);
RETURN_IF_ERROR(response.status());
std::string serialized_output = struct_pb::serialize<std::string>(*response);
uint32_t out_len = static_cast<uint32_t>(serialized_output.size());
RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, &out_len, sizeof(out_len)));
RETURN_IF_ERROR(WriteFd(STDOUT_FILENO, serialized_output.data(), out_len));
if (verbose_) {
std::cerr << "conformance-cpp: request=";
std::cerr << request.message_type << std::endl;
}
return false;
}
int main() {
Harness harness;
int total_runs = 0;
while (true) {
auto is_done = harness.ServeConformanceRequest();
if (!is_done.ok()) {
std::cerr << is_done.status();
}
if (*is_done) {
break;
}
total_runs++;
}
std::cerr << "conformance-struct_pb: received EOF from test runner after ";
std::cerr << total_runs << " tests" << std::endl;
return 0;
}
//
// the following code is useful for debugging when struct_pb conformance test
// failed.
//
std::string read_file(const std::string& tag, int index,
const std::string& suffix) {
std::string filename = tag + "/" + std::to_string(index) + "." + suffix;
std::ifstream ifs;
ifs.open(filename, std::ios::in | std::ios::binary);
std::size_t sz = fs::file_size(filename);
std::string buffer;
buffer.resize(sz);
ifs.read(buffer.data(), buffer.size());
return buffer;
}
int debug_struct_pb() {
Harness harness;
int index = 0;
while (index < 1283) {
// use data generated by conformance_cpp
auto in_buf = read_file("in", index, "bin");
auto out_buf = read_file("out", index, "bin");
ConformanceRequest req{};
bool ok =
struct_pb::internal::deserialize_to(req, in_buf.data(), in_buf.size());
if (!ok) {
std::cout << "ERROR: " << index << " decode" << std::endl;
return 1;
}
auto ret = harness.RunTest(req);
if (!ret.ok()) {
std::cout << "ERROR: " << index << " run test" << std::endl;
return 2;
}
auto buf = struct_pb::serialize<std::string>(*ret);
if (buf != out_buf) {
std::cout << "ERROR: " << index << " test fail" << std::endl;
return 3;
}
index++;
}
return 0;
}

View File

@ -1,526 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "conformance_test.h"
#include <stdarg.h>
#include <fstream>
#include <set>
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "conformance/conformance.pb.h"
#include "google/protobuf/message.h"
#include "google/protobuf/text_format.h"
#include "google/protobuf/util/field_comparator.h"
#include "google/protobuf/util/json_util.h"
#include "google/protobuf/util/message_differencer.h"
using conformance::ConformanceRequest;
using conformance::ConformanceResponse;
using conformance::WireFormat;
using google::protobuf::TextFormat;
using google::protobuf::util::DefaultFieldComparator;
using google::protobuf::util::MessageDifferencer;
using std::string;
namespace {
static string ToOctString(const string& binary_string) {
string oct_string;
for (size_t i = 0; i < binary_string.size(); i++) {
uint8_t c = binary_string.at(i);
uint8_t high = c / 64;
uint8_t mid = (c % 64) / 8;
uint8_t low = c % 8;
oct_string.push_back('\\');
oct_string.push_back('0' + high);
oct_string.push_back('0' + mid);
oct_string.push_back('0' + low);
}
return oct_string;
}
} // namespace
namespace google {
namespace protobuf {
ConformanceTestSuite::ConformanceRequestSetting::ConformanceRequestSetting(
ConformanceLevel level, conformance::WireFormat input_format,
conformance::WireFormat output_format,
conformance::TestCategory test_category, const Message& prototype_message,
const string& test_name, const string& input)
: level_(level),
input_format_(input_format),
output_format_(output_format),
prototype_message_(prototype_message),
prototype_message_for_compare_(prototype_message.New()),
test_name_(test_name) {
switch (input_format) {
case conformance::PROTOBUF: {
request_.set_protobuf_payload(input);
break;
}
case conformance::JSON: {
request_.set_json_payload(input);
break;
}
case conformance::JSPB: {
request_.set_jspb_payload(input);
break;
}
case conformance::TEXT_FORMAT: {
request_.set_text_payload(input);
break;
}
default:
GOOGLE_LOG(FATAL) << "Unspecified input format";
}
request_.set_test_category(test_category);
request_.set_message_type(prototype_message.GetDescriptor()->full_name());
request_.set_requested_output_format(output_format);
}
std::unique_ptr<Message>
ConformanceTestSuite::ConformanceRequestSetting::NewTestMessage() const {
return std::unique_ptr<Message>(prototype_message_for_compare_->New());
}
string ConformanceTestSuite::ConformanceRequestSetting::GetTestName() const {
string rname = prototype_message_.GetDescriptor()->file()->syntax() ==
FileDescriptor::SYNTAX_PROTO3
? "Proto3"
: "Proto2";
return absl::StrCat(ConformanceLevelToString(level_), ".", rname, ".",
InputFormatString(input_format_), ".", test_name_, ".",
OutputFormatString(output_format_));
}
string
ConformanceTestSuite::ConformanceRequestSetting::ConformanceLevelToString(
ConformanceLevel level) const {
switch (level) {
case REQUIRED:
return "Required";
case RECOMMENDED:
return "Recommended";
}
GOOGLE_LOG(FATAL) << "Unknown value: " << level;
return "";
}
string ConformanceTestSuite::ConformanceRequestSetting::InputFormatString(
conformance::WireFormat format) const {
switch (format) {
case conformance::PROTOBUF:
return "ProtobufInput";
case conformance::JSON:
return "JsonInput";
case conformance::TEXT_FORMAT:
return "TextFormatInput";
default:
GOOGLE_LOG(FATAL) << "Unspecified output format";
}
return "";
}
string ConformanceTestSuite::ConformanceRequestSetting::OutputFormatString(
conformance::WireFormat format) const {
switch (format) {
case conformance::PROTOBUF:
return "ProtobufOutput";
case conformance::JSON:
return "JsonOutput";
case conformance::TEXT_FORMAT:
return "TextFormatOutput";
default:
GOOGLE_LOG(FATAL) << "Unspecified output format";
}
return "";
}
void ConformanceTestSuite::TruncateDebugPayload(string* payload) {
if (payload != nullptr && payload->size() > 200) {
payload->resize(200);
payload->append("...(truncated)");
}
}
const ConformanceRequest ConformanceTestSuite::TruncateRequest(
const ConformanceRequest& request) {
ConformanceRequest debug_request(request);
switch (debug_request.payload_case()) {
case ConformanceRequest::kProtobufPayload:
TruncateDebugPayload(debug_request.mutable_protobuf_payload());
break;
case ConformanceRequest::kJsonPayload:
TruncateDebugPayload(debug_request.mutable_json_payload());
break;
case ConformanceRequest::kTextPayload:
TruncateDebugPayload(debug_request.mutable_text_payload());
break;
case ConformanceRequest::kJspbPayload:
TruncateDebugPayload(debug_request.mutable_jspb_payload());
break;
default:
// Do nothing.
break;
}
return debug_request;
}
const ConformanceResponse ConformanceTestSuite::TruncateResponse(
const ConformanceResponse& response) {
ConformanceResponse debug_response(response);
switch (debug_response.result_case()) {
case ConformanceResponse::kProtobufPayload:
TruncateDebugPayload(debug_response.mutable_protobuf_payload());
break;
case ConformanceResponse::kJsonPayload:
TruncateDebugPayload(debug_response.mutable_json_payload());
break;
case ConformanceResponse::kTextPayload:
TruncateDebugPayload(debug_response.mutable_text_payload());
break;
case ConformanceResponse::kJspbPayload:
TruncateDebugPayload(debug_response.mutable_jspb_payload());
break;
default:
// Do nothing.
break;
}
return debug_response;
}
void ConformanceTestSuite::ReportSuccess(const string& test_name) {
if (expected_to_fail_.erase(test_name) != 0) {
absl::StrAppendFormat(
&output_,
"ERROR: test %s is in the failure list, but test succeeded. "
"Remove it from the failure list.\n",
test_name);
unexpected_succeeding_tests_.insert(test_name);
}
successes_++;
}
void ConformanceTestSuite::ReportFailure(const string& test_name,
ConformanceLevel level,
const ConformanceRequest& request,
const ConformanceResponse& response,
std::string_view message) {
if (expected_to_fail_.erase(test_name) == 1) {
expected_failures_++;
if (!verbose_)
return;
}
else if (level == RECOMMENDED && !enforce_recommended_) {
absl::StrAppendFormat(&output_, "WARNING, test=%s: ", test_name);
}
else {
absl::StrAppendFormat(&output_, "ERROR, test=%s: ", test_name);
unexpected_failing_tests_.insert(test_name);
}
absl::StrAppendFormat(&output_, "%s, request=%s, response=%s\n", message,
TruncateRequest(request).ShortDebugString(),
TruncateResponse(response).ShortDebugString());
}
void ConformanceTestSuite::ReportSkip(const string& test_name,
const ConformanceRequest& request,
const ConformanceResponse& response) {
if (verbose_) {
absl::StrAppendFormat(
&output_, "SKIPPED, test=%s request=%s, response=%s\n", test_name,
request.ShortDebugString(), response.ShortDebugString());
}
skipped_.insert(test_name);
}
void ConformanceTestSuite::RunValidInputTest(
const ConformanceRequestSetting& setting,
const string& equivalent_text_format) {
std::unique_ptr<Message> reference_message(setting.NewTestMessage());
GOOGLE_CHECK(TextFormat::ParseFromString(equivalent_text_format,
reference_message.get()))
<< "Failed to parse data for test case: " << setting.GetTestName()
<< ", data: " << equivalent_text_format;
const string equivalent_wire_format = reference_message->SerializeAsString();
RunValidBinaryInputTest(setting, equivalent_wire_format);
}
void ConformanceTestSuite::RunValidBinaryInputTest(
const ConformanceRequestSetting& setting,
const string& equivalent_wire_format, bool require_same_wire_format) {
const ConformanceRequest& request = setting.GetRequest();
ConformanceResponse response;
RunTest(setting.GetTestName(), request, &response);
VerifyResponse(setting, equivalent_wire_format, response, true,
require_same_wire_format);
}
void ConformanceTestSuite::VerifyResponse(
const ConformanceRequestSetting& setting,
const string& equivalent_wire_format, const ConformanceResponse& response,
bool need_report_success, bool require_same_wire_format) {
std::unique_ptr<Message> test_message(setting.NewTestMessage());
const ConformanceRequest& request = setting.GetRequest();
const string& test_name = setting.GetTestName();
ConformanceLevel level = setting.GetLevel();
std::unique_ptr<Message> reference_message = setting.NewTestMessage();
GOOGLE_CHECK(reference_message->ParseFromString(equivalent_wire_format))
<< "Failed to parse wire data for test case: " << test_name;
switch (response.result_case()) {
case ConformanceResponse::RESULT_NOT_SET:
ReportFailure(test_name, level, request, response,
"Response didn't have any field in the Response.");
return;
case ConformanceResponse::kParseError:
case ConformanceResponse::kTimeoutError:
case ConformanceResponse::kRuntimeError:
case ConformanceResponse::kSerializeError:
ReportFailure(test_name, level, request, response,
"Failed to parse input or produce output.");
return;
case ConformanceResponse::kSkipped:
ReportSkip(test_name, request, response);
return;
default:
if (!ParseResponse(response, setting, test_message.get()))
return;
}
MessageDifferencer differencer;
DefaultFieldComparator field_comparator;
field_comparator.set_treat_nan_as_equal(true);
differencer.set_field_comparator(&field_comparator);
string differences;
differencer.ReportDifferencesToString(&differences);
bool check = false;
if (require_same_wire_format) {
GOOGLE_DCHECK_EQ(response.result_case(),
ConformanceResponse::kProtobufPayload);
const string& protobuf_payload = response.protobuf_payload();
check = equivalent_wire_format == protobuf_payload;
differences = absl::StrCat("Expect: ", ToOctString(equivalent_wire_format),
", but got: ", ToOctString(protobuf_payload));
}
else {
check = differencer.Compare(*reference_message, *test_message);
}
if (check) {
if (need_report_success) {
ReportSuccess(test_name);
}
}
else {
ReportFailure(
test_name, level, request, response,
absl::StrCat("Output was not equivalent to reference message: ",
differences));
}
}
void ConformanceTestSuite::RunTest(const string& test_name,
const ConformanceRequest& request,
ConformanceResponse* response) {
if (test_names_.insert(test_name).second == false) {
GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name;
}
string serialized_request;
string serialized_response;
request.SerializeToString(&serialized_request);
runner_->RunTest(test_name, serialized_request, &serialized_response);
if (!response->ParseFromString(serialized_response)) {
response->Clear();
response->set_runtime_error("response proto could not be parsed.");
}
if (verbose_) {
absl::StrAppendFormat(
&output_, "conformance test: name=%s, request=%s, response=%s\n",
test_name, TruncateRequest(request).ShortDebugString(),
TruncateResponse(*response).ShortDebugString());
}
}
bool ConformanceTestSuite::CheckSetEmpty(const std::set<string>& set_to_check,
const std::string& write_to_file,
const std::string& msg) {
if (set_to_check.empty()) {
return true;
}
else {
absl::StrAppendFormat(&output_, "\n");
absl::StrAppendFormat(&output_, "%s\n\n", msg);
for (std::string_view v : set_to_check) {
absl::StrAppendFormat(&output_, " %s\n", v);
}
absl::StrAppendFormat(&output_, "\n");
if (!write_to_file.empty()) {
std::string full_filename;
const std::string* filename = &write_to_file;
if (!output_dir_.empty()) {
full_filename = output_dir_;
if (*output_dir_.rbegin() != '/') {
full_filename.push_back('/');
}
full_filename += write_to_file;
filename = &full_filename;
}
std::ofstream os(*filename);
if (os) {
for (std::string_view v : set_to_check) {
os << v << "\n";
}
}
else {
absl::StrAppendFormat(&output_, "Failed to open file: %s\n", *filename);
}
}
return false;
}
}
string ConformanceTestSuite::WireFormatToString(WireFormat wire_format) {
switch (wire_format) {
case conformance::PROTOBUF:
return "PROTOBUF";
case conformance::JSON:
return "JSON";
case conformance::JSPB:
return "JSPB";
case conformance::TEXT_FORMAT:
return "TEXT_FORMAT";
case conformance::UNSPECIFIED:
return "UNSPECIFIED";
default:
GOOGLE_LOG(FATAL) << "unknown wire type: " << wire_format;
}
return "";
}
void ConformanceTestSuite::AddExpectedFailedTest(const std::string& test_name) {
expected_to_fail_.insert(test_name);
}
bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner,
std::string* output, const string& filename,
conformance::FailureSet* failure_list) {
runner_ = runner;
successes_ = 0;
expected_failures_ = 0;
skipped_.clear();
test_names_.clear();
unexpected_failing_tests_.clear();
unexpected_succeeding_tests_.clear();
output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n";
failure_list_filename_ = filename;
expected_to_fail_.clear();
for (const string& failure : failure_list->failure()) {
AddExpectedFailedTest(failure);
}
RunSuiteImpl();
bool ok = true;
if (!CheckSetEmpty(expected_to_fail_, "nonexistent_tests.txt",
"These tests were listed in the failure list, but they "
"don't exist. Remove them from the failure list by "
"running:\n"
" ./update_failure_list.py " +
failure_list_filename_ +
" --remove nonexistent_tests.txt")) {
ok = false;
}
if (!CheckSetEmpty(unexpected_failing_tests_, "failing_tests.txt",
"These tests failed. If they can't be fixed right now, "
"you can add them to the failure list so the overall "
"suite can succeed. Add them to the failure list by "
"running:\n"
" ./update_failure_list.py " +
failure_list_filename_ + " --add failing_tests.txt")) {
ok = false;
}
if (!CheckSetEmpty(unexpected_succeeding_tests_, "succeeding_tests.txt",
"These tests succeeded, even though they were listed in "
"the failure list. Remove them from the failure list "
"by running:\n"
" ./update_failure_list.py " +
failure_list_filename_ +
" --remove succeeding_tests.txt")) {
ok = false;
}
if (verbose_) {
CheckSetEmpty(skipped_, "",
"These tests were skipped (probably because support for some "
"features is not implemented)");
}
absl::StrAppendFormat(&output_,
"CONFORMANCE SUITE %s: %d successes, %zu skipped, "
"%d expected failures, %zu unexpected failures.\n",
ok ? "PASSED" : "FAILED", successes_, skipped_.size(),
expected_failures_, unexpected_failing_tests_.size());
absl::StrAppendFormat(&output_, "\n");
output->assign(output_);
return ok;
}
} // namespace protobuf
} // namespace google

View File

@ -1,331 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file defines a protocol for running the conformance test suite
// in-process. In other words, the suite itself will run in the same process as
// the code under test.
//
// For pros and cons of this approach, please see conformance.proto.
#ifndef CONFORMANCE_CONFORMANCE_TEST_H
#define CONFORMANCE_CONFORMANCE_TEST_H
#include <functional>
#include <string>
#include <vector>
#include "conformance/conformance.pb.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/util/type_resolver.h"
#include "google/protobuf/wire_format_lite.h"
namespace conformance {
class ConformanceRequest;
class ConformanceResponse;
} // namespace conformance
namespace protobuf_test_messages {
namespace proto3 {
class TestAllTypesProto3;
} // namespace proto3
} // namespace protobuf_test_messages
namespace google {
namespace protobuf {
class ConformanceTestSuite;
class ConformanceTestRunner {
public:
virtual ~ConformanceTestRunner() {}
// Call to run a single conformance test.
//
// "input" is a serialized conformance.ConformanceRequest.
// "output" should be set to a serialized conformance.ConformanceResponse.
//
// If there is any error in running the test itself, set "runtime_error" in
// the response.
virtual void RunTest(const std::string& test_name, const std::string& input,
std::string* output) = 0;
};
// Test runner that spawns the process being tested and communicates with it
// over a pipe.
class ForkPipeRunner : public ConformanceTestRunner {
public:
// Note: Run() doesn't take ownership of the pointers inside suites.
static int Run(int argc, char* argv[],
const std::vector<ConformanceTestSuite*>& suites);
ForkPipeRunner(const std::string& executable,
const std::vector<std::string>& executable_args,
bool performance)
: child_pid_(-1),
executable_(executable),
executable_args_(executable_args),
performance_(performance) {}
explicit ForkPipeRunner(const std::string& executable)
: child_pid_(-1), executable_(executable) {}
virtual ~ForkPipeRunner() {}
void RunTest(const std::string& test_name, const std::string& request,
std::string* response);
private:
void SpawnTestProgram();
void CheckedWrite(int fd, const void* buf, size_t len);
bool TryRead(int fd, void* buf, size_t len);
void CheckedRead(int fd, void* buf, size_t len);
int write_fd_;
int read_fd_;
pid_t child_pid_;
std::string executable_;
const std::vector<std::string> executable_args_;
bool performance_;
std::string current_test_name_;
};
// Class representing the test suite itself. To run it, implement your own
// class derived from ConformanceTestRunner, class derived from
// ConformanceTestSuite and then write code like:
//
// class MyConformanceTestSuite : public ConformanceTestSuite {
// public:
// void RunSuiteImpl() {
// // INSERT ACTUAL TESTS.
// }
// };
//
// class MyConformanceTestRunner : public ConformanceTestRunner {
// public:
// static int Run(int argc, char *argv[],
// ConformanceTestSuite* suite);
//
// private:
// virtual void RunTest(...) {
// // INSERT YOUR FRAMEWORK-SPECIFIC CODE HERE.
// }
// };
//
// int main() {
// MyConformanceTestSuite suite;
// MyConformanceTestRunner::Run(argc, argv, &suite);
// }
//
class ConformanceTestSuite {
public:
ConformanceTestSuite()
: verbose_(false),
performance_(false),
enforce_recommended_(false),
failure_list_flag_name_("--failure_list") {}
virtual ~ConformanceTestSuite() {}
void SetPerformance(bool performance) { performance_ = performance; }
void SetVerbose(bool verbose) { verbose_ = verbose; }
// Whether to require the testee to pass RECOMMENDED tests. By default failing
// a RECOMMENDED test case will not fail the entire suite but will only
// generated a warning. If this flag is set to true, RECOMMENDED tests will
// be treated the same way as REQUIRED tests and failing a RECOMMENDED test
// case will cause the entire test suite to fail as well. An implementation
// can enable this if it wants to be strictly conforming to protobuf spec.
// See the comments about ConformanceLevel below to learn more about the
// difference between REQUIRED and RECOMMENDED test cases.
void SetEnforceRecommended(bool value) { enforce_recommended_ = value; }
// Gets the flag name to the failure list file.
// By default, this would return --failure_list
std::string GetFailureListFlagName() { return failure_list_flag_name_; }
void SetFailureListFlagName(const std::string& failure_list_flag_name) {
failure_list_flag_name_ = failure_list_flag_name;
}
// Sets the path of the output directory.
void SetOutputDir(const char* output_dir) { output_dir_ = output_dir; }
// Run all the conformance tests against the given test runner.
// Test output will be stored in "output".
//
// Returns true if the set of failing tests was exactly the same as the
// failure list.
// The filename here is *only* used to create/format useful error messages for
// how to update the failure list. We do NOT read this file at all.
bool RunSuite(ConformanceTestRunner* runner, std::string* output,
const std::string& filename,
conformance::FailureSet* failure_list);
protected:
// Test cases are classified into a few categories:
// REQUIRED: the test case must be passed for an implementation to be
// interoperable with other implementations. For example, a
// parser implementation must accept both packed and unpacked
// form of repeated primitive fields.
// RECOMMENDED: the test case is not required for the implementation to
// be interoperable with other implementations, but is
// recommended for best performance and compatibility. For
// example, a proto3 serializer should serialize repeated
// primitive fields in packed form, but an implementation
// failing to do so will still be able to communicate with
// other implementations.
enum ConformanceLevel {
REQUIRED = 0,
RECOMMENDED = 1,
};
class ConformanceRequestSetting {
public:
ConformanceRequestSetting(ConformanceLevel level,
conformance::WireFormat input_format,
conformance::WireFormat output_format,
conformance::TestCategory test_category,
const Message& prototype_message,
const std::string& test_name,
const std::string& input);
virtual ~ConformanceRequestSetting() {}
std::unique_ptr<Message> NewTestMessage() const;
std::string GetTestName() const;
const conformance::ConformanceRequest& GetRequest() const {
return request_;
}
const ConformanceLevel GetLevel() const { return level_; }
std::string ConformanceLevelToString(ConformanceLevel level) const;
void SetPrintUnknownFields(bool print_unknown_fields) {
request_.set_print_unknown_fields(true);
}
void SetPrototypeMessageForCompare(const Message& message) {
prototype_message_for_compare_.reset(message.New());
}
protected:
virtual std::string InputFormatString(conformance::WireFormat format) const;
virtual std::string OutputFormatString(
conformance::WireFormat format) const;
conformance::ConformanceRequest request_;
private:
ConformanceLevel level_;
::conformance::WireFormat input_format_;
::conformance::WireFormat output_format_;
const Message& prototype_message_;
std::unique_ptr<Message> prototype_message_for_compare_;
std::string test_name_;
};
bool CheckSetEmpty(const std::set<std::string>& set_to_check,
const std::string& write_to_file, const std::string& msg);
std::string WireFormatToString(conformance::WireFormat wire_format);
// Parse payload in the response to the given message. Returns true on
// success.
virtual bool ParseResponse(const conformance::ConformanceResponse& response,
const ConformanceRequestSetting& setting,
Message* test_message) = 0;
void VerifyResponse(const ConformanceRequestSetting& setting,
const std::string& equivalent_wire_format,
const conformance::ConformanceResponse& response,
bool need_report_success, bool require_same_wire_format);
void TruncateDebugPayload(std::string* payload);
const conformance::ConformanceRequest TruncateRequest(
const conformance::ConformanceRequest& request);
const conformance::ConformanceResponse TruncateResponse(
const conformance::ConformanceResponse& response);
void ReportSuccess(const std::string& test_name);
void ReportFailure(const std::string& test_name, ConformanceLevel level,
const conformance::ConformanceRequest& request,
const conformance::ConformanceResponse& response,
std::string_view message);
void ReportSkip(const std::string& test_name,
const conformance::ConformanceRequest& request,
const conformance::ConformanceResponse& response);
void RunValidInputTest(const ConformanceRequestSetting& setting,
const std::string& equivalent_text_format);
void RunValidBinaryInputTest(const ConformanceRequestSetting& setting,
const std::string& equivalent_wire_format,
bool require_same_wire_format = false);
void RunTest(const std::string& test_name,
const conformance::ConformanceRequest& request,
conformance::ConformanceResponse* response);
void AddExpectedFailedTest(const std::string& test_name);
virtual void RunSuiteImpl() = 0;
ConformanceTestRunner* runner_;
int successes_;
int expected_failures_;
bool verbose_;
bool performance_;
bool enforce_recommended_;
std::string output_;
std::string output_dir_;
std::string failure_list_flag_name_;
std::string failure_list_filename_;
// The set of test names that are expected to fail in this run, but haven't
// failed yet.
std::set<std::string> expected_to_fail_;
// The set of test names that have been run. Used to ensure that there are no
// duplicate names in the suite.
std::set<std::string> test_names_;
// The set of tests that failed, but weren't expected to.
std::set<std::string> unexpected_failing_tests_;
// The set of tests that succeeded, but weren't expected to.
std::set<std::string> unexpected_succeeding_tests_;
// The set of tests that the testee opted out of;
std::set<std::string> skipped_;
};
} // namespace protobuf
} // namespace google
#endif // CONFORMANCE_CONFORMANCE_TEST_H

View File

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

View File

@ -1,407 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file contains a program for running the test suite in a separate
// process. The other alternative is to run the suite in-process. See
// conformance.proto for pros/cons of these two options.
//
// This program will fork the process under test and communicate with it over
// its stdin/stdout:
//
// +--------+ pipe +----------+
// | tester | <------> | testee |
// | | | |
// | C++ | | any lang |
// +--------+ +----------+
//
// The tester contains all of the test cases and their expected output.
// The testee is a simple program written in the target language that reads
// each test case and attempts to produce acceptable output for it.
//
// Every test consists of a ConformanceRequest/ConformanceResponse
// request/reply pair. The protocol on the pipe is simply:
//
// 1. tester sends 4-byte length N (little endian)
// 2. tester sends N bytes representing a ConformanceRequest proto
// 3. testee sends 4-byte length M (little endian)
// 4. testee sends M bytes representing a ConformanceResponse proto
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <future>
#include <vector>
// #include "absl/strings/str_format.h"
#include "conformance/conformance.pb.h"
#include "conformance_test.h"
using conformance::ConformanceResponse;
using google::protobuf::ConformanceTestSuite;
using std::string;
using std::vector;
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define GOOGLE_CHECK_SYSCALL(call) \
if (call < 0) { \
perror(#call " " __FILE__ ":" TOSTRING(__LINE__)); \
exit(1); \
}
namespace google {
namespace protobuf {
void ParseFailureList(const char *filename,
conformance::FailureSet *failure_list) {
std::ifstream infile(filename);
if (!infile.is_open()) {
fprintf(stderr, "Couldn't open failure list file: %s\n", filename);
exit(1);
}
for (string line; getline(infile, line);) {
// Remove whitespace.
line.erase(std::remove_if(line.begin(), line.end(), ::isspace), line.end());
// Remove comments.
line = line.substr(0, line.find("#"));
if (!line.empty()) {
failure_list->add_failure(line);
}
}
}
void UsageError() {
fprintf(stderr, "Usage: conformance-test-runner [options] <test-program>\n");
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr,
" --failure_list <filename> Use to specify list of tests\n");
fprintf(stderr,
" that are expected to fail. File\n");
fprintf(stderr,
" should contain one test name per\n");
fprintf(stderr,
" line. Use '#' for comments.\n");
fprintf(stderr,
" --text_format_failure_list <filename> Use to specify list \n");
fprintf(stderr,
" of tests that are expected to \n");
fprintf(stderr, " fail in the \n");
fprintf(stderr,
" text_format_conformance_suite. \n");
fprintf(stderr,
" File should contain one test name \n");
fprintf(stderr,
" per line. Use '#' for comments.\n");
fprintf(stderr,
" --enforce_recommended Enforce that recommended test\n");
fprintf(stderr,
" cases are also passing. Specify\n");
fprintf(stderr,
" this flag if you want to be\n");
fprintf(stderr,
" strictly conforming to protobuf\n");
fprintf(stderr, " spec.\n");
fprintf(stderr,
" --output_dir <dirname> Directory to write\n"
" output files.\n");
exit(1);
}
void ForkPipeRunner::RunTest(const std::string &test_name,
const std::string &request,
std::string *response) {
if (child_pid_ < 0) {
SpawnTestProgram();
}
current_test_name_ = test_name;
uint32_t len = request.size();
CheckedWrite(write_fd_, &len, sizeof(uint32_t));
CheckedWrite(write_fd_, request.c_str(), request.size());
if (!TryRead(read_fd_, &len, sizeof(uint32_t))) {
// We failed to read from the child, assume a crash and try to reap.
GOOGLE_LOG(INFO) << "Trying to reap child, pid=" << child_pid_;
int status = 0;
waitpid(child_pid_, &status, WEXITED);
string error_msg;
conformance::ConformanceResponse response_obj;
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == 0) {
error_msg = "child timed out, killed by signal " +
std::to_string(WTERMSIG(status));
response_obj.set_timeout_error(error_msg);
}
else {
error_msg =
"child exited, status=" + std::to_string(WEXITSTATUS(status));
response_obj.set_runtime_error(error_msg);
}
}
else if (WIFSIGNALED(status)) {
error_msg = "child killed by signal " + std::to_string(WTERMSIG(status));
}
GOOGLE_LOG(INFO) << error_msg;
child_pid_ = -1;
response_obj.SerializeToString(response);
return;
}
response->resize(len);
CheckedRead(read_fd_, (void *)response->c_str(), len);
}
int ForkPipeRunner::Run(int argc, char *argv[],
const std::vector<ConformanceTestSuite *> &suites) {
if (suites.empty()) {
fprintf(stderr, "No test suites found.\n");
return EXIT_FAILURE;
}
bool all_ok = true;
for (ConformanceTestSuite *suite : suites) {
string program;
std::vector<string> program_args;
string failure_list_filename;
conformance::FailureSet failure_list;
bool performance = false;
for (int arg = 1; arg < argc; ++arg) {
if (strcmp(argv[arg], suite->GetFailureListFlagName().c_str()) == 0) {
if (++arg == argc)
UsageError();
failure_list_filename = argv[arg];
ParseFailureList(argv[arg], &failure_list);
}
else if (strcmp(argv[arg], "--performance") == 0) {
performance = true;
suite->SetPerformance(true);
}
else if (strcmp(argv[arg], "--verbose") == 0) {
suite->SetVerbose(true);
}
else if (strcmp(argv[arg], "--enforce_recommended") == 0) {
suite->SetEnforceRecommended(true);
}
else if (strcmp(argv[arg], "--output_dir") == 0) {
if (++arg == argc)
UsageError();
suite->SetOutputDir(argv[arg]);
}
else if (argv[arg][0] == '-') {
bool recognized_flag = false;
for (ConformanceTestSuite *suite : suites) {
if (strcmp(argv[arg], suite->GetFailureListFlagName().c_str()) == 0) {
if (++arg == argc)
UsageError();
recognized_flag = true;
}
}
if (!recognized_flag) {
fprintf(stderr, "Unknown option: %s\n", argv[arg]);
UsageError();
}
}
else {
program += argv[arg++];
while (arg < argc) {
program_args.push_back(argv[arg]);
arg++;
}
}
}
ForkPipeRunner runner(program, program_args, performance);
std::string output;
all_ok = all_ok && suite->RunSuite(&runner, &output, failure_list_filename,
&failure_list);
fwrite(output.c_str(), 1, output.size(), stderr);
}
return all_ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
// TODO(haberman): make this work on Windows, instead of using these
// UNIX-specific APIs.
//
// There is a platform-agnostic API in
// src/google/protobuf/compiler/subprocess.h
//
// However that API only supports sending a single message to the subprocess.
// We really want to be able to send messages and receive responses one at a
// time:
//
// 1. Spawning a new process for each test would take way too long for thousands
// of tests and subprocesses like java that can take 100ms or more to start
// up.
//
// 2. Sending all the tests in one big message and receiving all results in one
// big message would take away our visibility about which test(s) caused a
// crash or other fatal error. It would also give us only a single failure
// instead of all of them.
void ForkPipeRunner::SpawnTestProgram() {
int toproc_pipe_fd[2];
int fromproc_pipe_fd[2];
if (pipe(toproc_pipe_fd) < 0 || pipe(fromproc_pipe_fd) < 0) {
perror("pipe");
exit(1);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid) {
// Parent.
GOOGLE_CHECK_SYSCALL(close(toproc_pipe_fd[0]));
GOOGLE_CHECK_SYSCALL(close(fromproc_pipe_fd[1]));
write_fd_ = toproc_pipe_fd[1];
read_fd_ = fromproc_pipe_fd[0];
child_pid_ = pid;
}
else {
// Child.
GOOGLE_CHECK_SYSCALL(close(STDIN_FILENO));
GOOGLE_CHECK_SYSCALL(close(STDOUT_FILENO));
GOOGLE_CHECK_SYSCALL(dup2(toproc_pipe_fd[0], STDIN_FILENO));
GOOGLE_CHECK_SYSCALL(dup2(fromproc_pipe_fd[1], STDOUT_FILENO));
GOOGLE_CHECK_SYSCALL(close(toproc_pipe_fd[0]));
GOOGLE_CHECK_SYSCALL(close(fromproc_pipe_fd[1]));
GOOGLE_CHECK_SYSCALL(close(toproc_pipe_fd[1]));
GOOGLE_CHECK_SYSCALL(close(fromproc_pipe_fd[0]));
std::unique_ptr<char[]> executable(new char[executable_.size() + 1]);
memcpy(executable.get(), executable_.c_str(), executable_.size());
executable[executable_.size()] = '\0';
std::vector<const char *> argv;
argv.push_back(executable.get());
GOOGLE_LOG(INFO) << argv[0];
for (size_t i = 0; i < executable_args_.size(); ++i) {
argv.push_back(executable_args_[i].c_str());
GOOGLE_LOG(INFO) << executable_args_[i];
}
argv.push_back(nullptr);
// Never returns.
GOOGLE_CHECK_SYSCALL(
execv(executable.get(), const_cast<char **>(argv.data())));
}
}
void ForkPipeRunner::CheckedWrite(int fd, const void *buf, size_t len) {
if (static_cast<size_t>(write(fd, buf, len)) != len) {
GOOGLE_LOG(FATAL) << current_test_name_
<< ": error writing to test program: " << strerror(errno);
}
}
bool ForkPipeRunner::TryRead(int fd, void *buf, size_t len) {
size_t ofs = 0;
while (len > 0) {
std::future<ssize_t> future = std::async(
std::launch::async,
[](int fd, void *buf, size_t ofs, size_t len) {
return read(fd, (char *)buf + ofs, len);
},
fd, buf, ofs, len);
std::future_status status;
if (performance_) {
status = future.wait_for(std::chrono::seconds(5));
if (status == std::future_status::timeout) {
GOOGLE_LOG(ERROR) << current_test_name_
<< ": timeout from test program";
kill(child_pid_, SIGQUIT);
// TODO(sandyzhang): Only log in flag-guarded mode, since reading output
// from SIGQUIT is slow and verbose.
std::vector<char> err;
err.resize(5000);
ssize_t err_bytes_read;
size_t err_ofs = 0;
do {
err_bytes_read =
read(fd, (void *)&err[err_ofs], err.size() - err_ofs);
err_ofs += err_bytes_read;
} while (err_bytes_read > 0 && err_ofs < err.size());
GOOGLE_LOG(ERROR) << "child_pid_=" << child_pid_ << " SIGQUIT: \n"
<< &err[0];
return false;
}
}
else {
future.wait();
}
ssize_t bytes_read = future.get();
if (bytes_read == 0) {
GOOGLE_LOG(ERROR) << current_test_name_
<< ": unexpected EOF from test program";
return false;
}
else if (bytes_read < 0) {
GOOGLE_LOG(ERROR) << current_test_name_
<< ": error reading from test program: "
<< strerror(errno);
return false;
}
len -= bytes_read;
ofs += bytes_read;
}
return true;
}
void ForkPipeRunner::CheckedRead(int fd, void *buf, size_t len) {
if (!TryRead(fd, buf, len)) {
GOOGLE_LOG(FATAL) << current_test_name_
<< ": error reading from test program: "
<< strerror(errno);
}
}
} // namespace protobuf
} // namespace google

View File

@ -1,9 +0,0 @@
# This is the list of conformance tests that are known to fail for the C++
# implementation right now. These should be fixed.
#
# By listing them here we can keep tabs on which ones are failing and be sure
# that we don't introduce regressions in other tests.
#
# TODO(haberman): insert links to corresponding bugs tracking the issue.
# Should we use GitHub issues or the Google-internal bug tracker?

View File

@ -1,161 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option go_package = "google.golang.org/protobuf/types/known/anypb";
option java_package = "com.google.protobuf";
option java_outer_classname = "AnyProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// `Any` contains an arbitrary serialized protocol buffer message along with a
// URL that describes the type of the serialized message.
//
// Protobuf library provides support to pack/unpack Any values in the form
// of utility functions or additional generated methods of the Any type.
//
// Example 1: Pack and unpack a message in C++.
//
// Foo foo = ...;
// Any any;
// any.PackFrom(foo);
// ...
// if (any.UnpackTo(&foo)) {
// ...
// }
//
// Example 2: Pack and unpack a message in Java.
//
// Foo foo = ...;
// Any any = Any.pack(foo);
// ...
// if (any.is(Foo.class)) {
// foo = any.unpack(Foo.class);
// }
// // or ...
// if (any.isSameTypeAs(Foo.getDefaultInstance())) {
// foo = any.unpack(Foo.getDefaultInstance());
// }
//
// Example 3: Pack and unpack a message in Python.
//
// foo = Foo(...)
// any = Any()
// any.Pack(foo)
// ...
// if any.Is(Foo.DESCRIPTOR):
// any.Unpack(foo)
// ...
//
// Example 4: Pack and unpack a message in Go
//
// foo := &pb.Foo{...}
// any, err := anypb.New(foo)
// if err != nil {
// ...
// }
// ...
// foo := &pb.Foo{}
// if err := any.UnmarshalTo(foo); err != nil {
// ...
// }
//
// The pack methods provided by protobuf library will by default use
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
// methods only use the fully qualified type name after the last '/'
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
// name "y.z".
//
// JSON
//
// The JSON representation of an `Any` value uses the regular
// representation of the deserialized, embedded message, with an
// additional field `@type` which contains the type URL. Example:
//
// package google.profile;
// message Person {
// string first_name = 1;
// string last_name = 2;
// }
//
// {
// "@type": "type.googleapis.com/google.profile.Person",
// "firstName": <string>,
// "lastName": <string>
// }
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
// `value` which holds the custom JSON in addition to the `@type`
// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
// "value": "1.212s"
// }
//
message Any {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. This string must contain at least
// one "/" character. The last segment of the URL's path must represent
// the fully qualified name of the type (as in
// `path/google.protobuf.Duration`). The name should be in a canonical form
// (e.g., leading "." is not accepted).
//
// In practice, teams usually precompile into the binary all types that they
// expect it to use in the context of Any. However, for URLs which use the
// scheme `http`, `https`, or no scheme, one can optionally set up a type
// server that maps type URLs to message definitions as follows:
//
// * If no scheme is provided, `https` is assumed.
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
// lookup. Therefore, binary compatibility needs to be preserved
// on changes to types. (Use versioned type names to manage
// breaking changes.)
//
// Note: this functionality is not currently available in the official
// protobuf release, and it is not used for type URLs beginning with
// type.googleapis.com.
//
// Schemes other than `http`, `https` (or the empty scheme) might be
// used with implementation specific semantics.
//
string type_url = 1;
// Must be a valid serialized protocol buffer of the above specified type.
bytes value = 2;
}

View File

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

View File

@ -1,115 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/durationpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DurationProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// A Duration represents a signed, fixed-length span of time represented
// as a count of seconds and fractions of seconds at nanosecond
// resolution. It is independent of any calendar and concepts like "day"
// or "month". It is related to Timestamp in that the difference between
// two Timestamp values is a Duration and it can be added or subtracted
// from a Timestamp. Range is approximately +-10,000 years.
//
// # Examples
//
// Example 1: Compute Duration from two Timestamps in pseudo code.
//
// Timestamp start = ...;
// Timestamp end = ...;
// Duration duration = ...;
//
// duration.seconds = end.seconds - start.seconds;
// duration.nanos = end.nanos - start.nanos;
//
// if (duration.seconds < 0 && duration.nanos > 0) {
// duration.seconds += 1;
// duration.nanos -= 1000000000;
// } else if (duration.seconds > 0 && duration.nanos < 0) {
// duration.seconds -= 1;
// duration.nanos += 1000000000;
// }
//
// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
//
// Timestamp start = ...;
// Duration duration = ...;
// Timestamp end = ...;
//
// end.seconds = start.seconds + duration.seconds;
// end.nanos = start.nanos + duration.nanos;
//
// if (end.nanos < 0) {
// end.seconds -= 1;
// end.nanos += 1000000000;
// } else if (end.nanos >= 1000000000) {
// end.seconds += 1;
// end.nanos -= 1000000000;
// }
//
// Example 3: Compute Duration from datetime.timedelta in Python.
//
// td = datetime.timedelta(days=3, minutes=10)
// duration = Duration()
// duration.FromTimedelta(td)
//
// # JSON Mapping
//
// In JSON format, the Duration type is encoded as a string rather than an
// object, where the string ends in the suffix "s" (indicating seconds) and
// is preceded by the number of seconds, with nanoseconds expressed as
// fractional seconds. For example, 3 seconds with 0 nanoseconds should be
// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
// be expressed in JSON format as "3.000000001s", and 3 seconds and 1
// microsecond should be expressed in JSON format as "3.000001s".
//
message Duration {
// Signed seconds of the span of time. Must be from -315,576,000,000
// to +315,576,000,000 inclusive. Note: these bounds are computed from:
// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
int64 seconds = 1;
// Signed fractions of a second at nanosecond resolution of the span
// of time. Durations less than one second are represented with a 0
// `seconds` field and a positive or negative `nanos` field. For durations
// of one second or more, a non-zero value for the `nanos` field must be
// of the same sign as the `seconds` field. Must be from -999,999,999
// to +999,999,999 inclusive.
int32 nanos = 2;
}

View File

@ -1,245 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option java_package = "com.google.protobuf";
option java_outer_classname = "FieldMaskProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb";
option cc_enable_arenas = true;
// `FieldMask` represents a set of symbolic field paths, for example:
//
// paths: "f.a"
// paths: "f.b.d"
//
// Here `f` represents a field in some root message, `a` and `b`
// fields in the message found in `f`, and `d` a field found in the
// message in `f.b`.
//
// Field masks are used to specify a subset of fields that should be
// returned by a get operation or modified by an update operation.
// Field masks also have a custom JSON encoding (see below).
//
// # Field Masks in Projections
//
// When used in the context of a projection, a response message or
// sub-message is filtered by the API to only contain those fields as
// specified in the mask. For example, if the mask in the previous
// example is applied to a response message as follows:
//
// f {
// a : 22
// b {
// d : 1
// x : 2
// }
// y : 13
// }
// z: 8
//
// The result will not contain specific values for fields x,y and z
// (their value will be set to the default, and omitted in proto text
// output):
//
//
// f {
// a : 22
// b {
// d : 1
// }
// }
//
// A repeated field is not allowed except at the last position of a
// paths string.
//
// If a FieldMask object is not present in a get operation, the
// operation applies to all fields (as if a FieldMask of all fields
// had been specified).
//
// Note that a field mask does not necessarily apply to the
// top-level response message. In case of a REST get operation, the
// field mask applies directly to the response, but in case of a REST
// list operation, the mask instead applies to each individual message
// in the returned resource list. In case of a REST custom method,
// other definitions may be used. Where the mask applies will be
// clearly documented together with its declaration in the API. In
// any case, the effect on the returned resource/resources is required
// behavior for APIs.
//
// # Field Masks in Update Operations
//
// A field mask in update operations specifies which fields of the
// targeted resource are going to be updated. The API is required
// to only change the values of the fields as specified in the mask
// and leave the others untouched. If a resource is passed in to
// describe the updated values, the API ignores the values of all
// fields not covered by the mask.
//
// If a repeated field is specified for an update operation, new values will
// be appended to the existing repeated field in the target resource. Note that
// a repeated field is only allowed in the last position of a `paths` string.
//
// If a sub-message is specified in the last position of the field mask for an
// update operation, then new value will be merged into the existing sub-message
// in the target resource.
//
// For example, given the target message:
//
// f {
// b {
// d: 1
// x: 2
// }
// c: [1]
// }
//
// And an update message:
//
// f {
// b {
// d: 10
// }
// c: [2]
// }
//
// then if the field mask is:
//
// paths: ["f.b", "f.c"]
//
// then the result will be:
//
// f {
// b {
// d: 10
// x: 2
// }
// c: [1, 2]
// }
//
// An implementation may provide options to override this default behavior for
// repeated and message fields.
//
// In order to reset a field's value to the default, the field must
// be in the mask and set to the default value in the provided resource.
// Hence, in order to reset all fields of a resource, provide a default
// instance of the resource and set all fields in the mask, or do
// not provide a mask as described below.
//
// If a field mask is not present on update, the operation applies to
// all fields (as if a field mask of all fields has been specified).
// Note that in the presence of schema evolution, this may mean that
// fields the client does not know and has therefore not filled into
// the request will be reset to their default. If this is unwanted
// behavior, a specific service may require a client to always specify
// a field mask, producing an error if not.
//
// As with get operations, the location of the resource which
// describes the updated values in the request message depends on the
// operation kind. In any case, the effect of the field mask is
// required to be honored by the API.
//
// ## Considerations for HTTP REST
//
// The HTTP kind of an update operation which uses a field mask must
// be set to PATCH instead of PUT in order to satisfy HTTP semantics
// (PUT must only be used for full updates).
//
// # JSON Encoding of Field Masks
//
// In JSON, a field mask is encoded as a single string where paths are
// separated by a comma. Fields name in each path are converted
// to/from lower-camel naming conventions.
//
// As an example, consider the following message declarations:
//
// message Profile {
// User user = 1;
// Photo photo = 2;
// }
// message User {
// string display_name = 1;
// string address = 2;
// }
//
// In proto a field mask for `Profile` may look as such:
//
// mask {
// paths: "user.display_name"
// paths: "photo"
// }
//
// In JSON, the same mask is represented as below:
//
// {
// mask: "user.displayName,photo"
// }
//
// # Field Masks and Oneof Fields
//
// Field masks treat fields in oneofs just as regular fields. Consider the
// following message:
//
// message SampleMessage {
// oneof test_oneof {
// string name = 4;
// SubMessage sub_message = 9;
// }
// }
//
// The field mask can be:
//
// mask {
// paths: "name"
// }
//
// Or:
//
// mask {
// paths: "sub_message"
// }
//
// Note that oneof type names ("test_oneof" in this case) cannot be used in
// paths.
//
// ## Field Mask Verification
//
// The implementation of any API method which has a FieldMask type field in the
// request should verify the included field paths, and return an
// `INVALID_ARGUMENT` error if any path is unmappable.
message FieldMask {
// The set of field mask paths.
repeated string paths = 1;
}

View File

@ -1,48 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option java_package = "com.google.protobuf";
option java_outer_classname = "SourceContextProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
// `SourceContext` represents information about the source of a
// protobuf element, like the file in which it is defined.
message SourceContext {
// The path-qualified name of the .proto file that contained the associated
// protobuf element. For example: `"google/protobuf/source_context.proto"`.
string file_name = 1;
}

View File

@ -1,95 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/structpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "StructProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// `Struct` represents a structured data value, consisting of fields
// which map to dynamically typed values. In some languages, `Struct`
// might be supported by a native representation. For example, in
// scripting languages like JS a struct is represented as an
// object. The details of that representation are described together
// with the proto support for the language.
//
// The JSON representation for `Struct` is JSON object.
message Struct {
// Unordered map of dynamically typed values.
map<string, Value> fields = 1;
}
// `Value` represents a dynamically typed value which can be either
// null, a number, a string, a boolean, a recursive struct value, or a
// list of values. A producer of value is expected to set one of these
// variants. Absence of any variant indicates an error.
//
// The JSON representation for `Value` is JSON value.
message Value {
// The kind of value.
oneof kind {
// Represents a null value.
NullValue null_value = 1;
// Represents a double value.
double number_value = 2;
// Represents a string value.
string string_value = 3;
// Represents a boolean value.
bool bool_value = 4;
// Represents a structured value.
Struct struct_value = 5;
// Represents a repeated `Value`.
ListValue list_value = 6;
}
}
// `NullValue` is a singleton enumeration to represent the null value for the
// `Value` type union.
//
// The JSON representation for `NullValue` is JSON `null`.
enum NullValue {
// Null value.
NULL_VALUE = 0;
}
// `ListValue` is a wrapper around a repeated field of values.
//
// The JSON representation for `ListValue` is JSON array.
message ListValue {
// Repeated field of dynamically typed values.
repeated Value values = 1;
}

View File

@ -1,303 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Test schema for proto2 messages. This test schema is used by:
//
// - conformance tests
//
// LINT: ALLOW_GROUPS
syntax = "proto2";
package protobuf_test_messages.proto2;
option java_package = "com.google.protobuf_test_messages.proto2";
option objc_class_prefix = "Proto2";
// This is the default, but we specify it here explicitly.
option optimize_for = SPEED;
option cc_enable_arenas = true;
// This proto includes every type of field in both singular and repeated
// forms.
//
// Also, crucially, all messages and enums in this file are eventually
// submessages of this message. So for example, a fuzz test of TestAllTypes
// could trigger bugs that occur in any message type in this file. We verify
// this stays true in a unit test.
message TestAllTypesProto2 {
message NestedMessage {
optional int32 a = 1;
optional TestAllTypesProto2 corecursive = 2;
}
enum NestedEnum {
FOO = 0;
BAR = 1;
BAZ = 2;
NEG = -1; // Intentionally negative.
}
// Singular
optional int32 optional_int32 = 1;
optional int64 optional_int64 = 2;
optional uint32 optional_uint32 = 3;
optional uint64 optional_uint64 = 4;
optional sint32 optional_sint32 = 5;
optional sint64 optional_sint64 = 6;
optional fixed32 optional_fixed32 = 7;
optional fixed64 optional_fixed64 = 8;
optional sfixed32 optional_sfixed32 = 9;
optional sfixed64 optional_sfixed64 = 10;
optional float optional_float = 11;
optional double optional_double = 12;
optional bool optional_bool = 13;
optional string optional_string = 14;
optional bytes optional_bytes = 15;
optional NestedMessage optional_nested_message = 18;
optional ForeignMessageProto2 optional_foreign_message = 19;
optional NestedEnum optional_nested_enum = 21;
optional ForeignEnumProto2 optional_foreign_enum = 22;
optional string optional_string_piece = 24 [ctype = STRING_PIECE];
optional string optional_cord = 25 [ctype = CORD];
optional TestAllTypesProto2 recursive_message = 27;
// 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;
repeated NestedMessage repeated_nested_message = 48;
repeated ForeignMessageProto2 repeated_foreign_message = 49;
repeated NestedEnum repeated_nested_enum = 51;
repeated ForeignEnumProto2 repeated_foreign_enum = 52;
repeated string repeated_string_piece = 54 [ctype = STRING_PIECE];
repeated string repeated_cord = 55 [ctype = CORD];
// Packed
repeated int32 packed_int32 = 75 [packed = true];
repeated int64 packed_int64 = 76 [packed = true];
repeated uint32 packed_uint32 = 77 [packed = true];
repeated uint64 packed_uint64 = 78 [packed = true];
repeated sint32 packed_sint32 = 79 [packed = true];
repeated sint64 packed_sint64 = 80 [packed = true];
repeated fixed32 packed_fixed32 = 81 [packed = true];
repeated fixed64 packed_fixed64 = 82 [packed = true];
repeated sfixed32 packed_sfixed32 = 83 [packed = true];
repeated sfixed64 packed_sfixed64 = 84 [packed = true];
repeated float packed_float = 85 [packed = true];
repeated double packed_double = 86 [packed = true];
repeated bool packed_bool = 87 [packed = true];
repeated NestedEnum packed_nested_enum = 88 [packed = true];
// Unpacked
repeated int32 unpacked_int32 = 89 [packed = false];
repeated int64 unpacked_int64 = 90 [packed = false];
repeated uint32 unpacked_uint32 = 91 [packed = false];
repeated uint64 unpacked_uint64 = 92 [packed = false];
repeated sint32 unpacked_sint32 = 93 [packed = false];
repeated sint64 unpacked_sint64 = 94 [packed = false];
repeated fixed32 unpacked_fixed32 = 95 [packed = false];
repeated fixed64 unpacked_fixed64 = 96 [packed = false];
repeated sfixed32 unpacked_sfixed32 = 97 [packed = false];
repeated sfixed64 unpacked_sfixed64 = 98 [packed = false];
repeated float unpacked_float = 99 [packed = false];
repeated double unpacked_double = 100 [packed = false];
repeated bool unpacked_bool = 101 [packed = false];
repeated NestedEnum unpacked_nested_enum = 102 [packed = false];
// Map
map<int32, int32> map_int32_int32 = 56;
map<int64, int64> map_int64_int64 = 57;
map<uint32, uint32> map_uint32_uint32 = 58;
map<uint64, uint64> map_uint64_uint64 = 59;
map<sint32, sint32> map_sint32_sint32 = 60;
map<sint64, sint64> map_sint64_sint64 = 61;
map<fixed32, fixed32> map_fixed32_fixed32 = 62;
map<fixed64, fixed64> map_fixed64_fixed64 = 63;
map<sfixed32, sfixed32> map_sfixed32_sfixed32 = 64;
map<sfixed64, sfixed64> map_sfixed64_sfixed64 = 65;
map<int32, float> map_int32_float = 66;
map<int32, double> map_int32_double = 67;
map<bool, bool> map_bool_bool = 68;
map<string, string> map_string_string = 69;
map<string, bytes> map_string_bytes = 70;
map<string, NestedMessage> map_string_nested_message = 71;
map<string, ForeignMessageProto2> map_string_foreign_message = 72;
map<string, NestedEnum> map_string_nested_enum = 73;
map<string, ForeignEnumProto2> map_string_foreign_enum = 74;
oneof oneof_field {
uint32 oneof_uint32 = 111;
NestedMessage oneof_nested_message = 112;
string oneof_string = 113;
bytes oneof_bytes = 114;
bool oneof_bool = 115;
uint64 oneof_uint64 = 116;
float oneof_float = 117;
double oneof_double = 118;
NestedEnum oneof_enum = 119;
}
// extensions
extensions 120 to 200;
// groups
optional group Data = 201 {
optional int32 group_int32 = 202;
optional uint32 group_uint32 = 203;
}
// default values
optional int32 default_int32 = 241 [default = -123456789];
optional int64 default_int64 = 242 [default = -9123456789123456789];
optional uint32 default_uint32 = 243 [default = 2123456789];
optional uint64 default_uint64 = 244 [default = 10123456789123456789];
optional sint32 default_sint32 = 245 [default = -123456789];
optional sint64 default_sint64 = 246 [default = -9123456789123456789];
optional fixed32 default_fixed32 = 247 [default = 2123456789];
optional fixed64 default_fixed64 = 248 [default = 10123456789123456789];
optional sfixed32 default_sfixed32 = 249 [default = -123456789];
optional sfixed64 default_sfixed64 = 250 [default = -9123456789123456789];
optional float default_float = 251 [default = 9e9];
optional double default_double = 252 [default = 7e22];
optional bool default_bool = 253 [default = true];
optional string default_string = 254 [default = "Rosebud"];
optional bytes default_bytes = 255 [default = "joshua"];
// Test field-name-to-JSON-name convention.
// (protobuf says names can be any valid C/C++ identifier.)
optional int32 fieldname1 = 401;
optional int32 field_name2 = 402;
optional int32 _field_name3 = 403;
optional int32 field__name4_ = 404;
optional int32 field0name5 = 405;
optional int32 field_0_name6 = 406;
optional int32 fieldName7 = 407;
optional int32 FieldName8 = 408;
optional int32 field_Name9 = 409;
optional int32 Field_Name10 = 410;
optional int32 FIELD_NAME11 = 411;
optional int32 FIELD_name12 = 412;
optional int32 __field_name13 = 413;
optional int32 __Field_name14 = 414;
optional int32 field__name15 = 415;
optional int32 field__Name16 = 416;
optional int32 field_name17__ = 417;
optional int32 Field_name18__ = 418;
// Reserved for unknown fields test.
reserved 1000 to 9999;
// message_set test case.
message MessageSetCorrect {
option message_set_wire_format = true;
extensions 4 to max;
}
message MessageSetCorrectExtension1 {
extend MessageSetCorrect {
optional MessageSetCorrectExtension1 message_set_extension = 1547769;
}
optional string str = 25;
}
message MessageSetCorrectExtension2 {
extend MessageSetCorrect {
optional MessageSetCorrectExtension2 message_set_extension = 4135312;
}
optional int32 i = 9;
}
}
message ForeignMessageProto2 {
optional int32 c = 1;
}
enum ForeignEnumProto2 {
FOREIGN_FOO = 0;
FOREIGN_BAR = 1;
FOREIGN_BAZ = 2;
}
extend TestAllTypesProto2 {
optional int32 extension_int32 = 120;
}
message UnknownToTestAllTypes {
optional int32 optional_int32 = 1001;
optional string optional_string = 1002;
optional ForeignMessageProto2 nested_message = 1003;
optional group OptionalGroup = 1004 {
optional int32 a = 1;
}
optional bool optional_bool = 1006;
repeated int32 repeated_int32 = 1011;
}
message NullHypothesisProto2 {}
message EnumOnlyProto2 {
enum Bool {
kFalse = 0;
kTrue = 1;
}
}
message OneStringProto2 {
optional string data = 1;
}
message ProtoWithKeywords {
optional int32 inline = 1;
// optional string concept = 2;
// repeated string requires = 3;
}

View File

@ -1,288 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Test schema for proto3 messages. This test schema is used by:
//
// - benchmarks
// - fuzz tests
// - conformance tests
//
syntax = "proto3";
package protobuf_test_messages.proto3;
option java_package = "com.google.protobuf_test_messages.proto3";
option objc_class_prefix = "Proto3";
// This is the default, but we specify it here explicitly.
option optimize_for = SPEED;
import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
option cc_enable_arenas = true;
// This proto includes every type of field in both singular and repeated
// forms.
//
// Also, crucially, all messages and enums in this file are eventually
// submessages of this message. So for example, a fuzz test of TestAllTypes
// could trigger bugs that occur in any message type in this file. We verify
// this stays true in a unit test.
message TestAllTypesProto3 {
message NestedMessage {
int32 a = 1;
TestAllTypesProto3 corecursive = 2;
}
enum NestedEnum {
FOO = 0;
BAR = 1;
BAZ = 2;
NEG = -1; // Intentionally negative.
}
enum AliasedEnum {
option allow_alias = true;
ALIAS_FOO = 0;
ALIAS_BAR = 1;
ALIAS_BAZ = 2;
MOO = 2;
moo = 2;
bAz = 2;
}
// 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;
NestedMessage optional_nested_message = 18;
ForeignMessage optional_foreign_message = 19;
NestedEnum optional_nested_enum = 21;
ForeignEnum optional_foreign_enum = 22;
AliasedEnum optional_aliased_enum = 23;
string optional_string_piece = 24 [ctype = STRING_PIECE];
string optional_cord = 25 [ctype = CORD];
TestAllTypesProto3 recursive_message = 27;
// 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;
repeated NestedMessage repeated_nested_message = 48;
repeated ForeignMessage repeated_foreign_message = 49;
repeated NestedEnum repeated_nested_enum = 51;
repeated ForeignEnum repeated_foreign_enum = 52;
repeated string repeated_string_piece = 54 [ctype = STRING_PIECE];
repeated string repeated_cord = 55 [ctype = CORD];
// Packed
repeated int32 packed_int32 = 75 [packed = true];
repeated int64 packed_int64 = 76 [packed = true];
repeated uint32 packed_uint32 = 77 [packed = true];
repeated uint64 packed_uint64 = 78 [packed = true];
repeated sint32 packed_sint32 = 79 [packed = true];
repeated sint64 packed_sint64 = 80 [packed = true];
repeated fixed32 packed_fixed32 = 81 [packed = true];
repeated fixed64 packed_fixed64 = 82 [packed = true];
repeated sfixed32 packed_sfixed32 = 83 [packed = true];
repeated sfixed64 packed_sfixed64 = 84 [packed = true];
repeated float packed_float = 85 [packed = true];
repeated double packed_double = 86 [packed = true];
repeated bool packed_bool = 87 [packed = true];
repeated NestedEnum packed_nested_enum = 88 [packed = true];
// Unpacked
repeated int32 unpacked_int32 = 89 [packed = false];
repeated int64 unpacked_int64 = 90 [packed = false];
repeated uint32 unpacked_uint32 = 91 [packed = false];
repeated uint64 unpacked_uint64 = 92 [packed = false];
repeated sint32 unpacked_sint32 = 93 [packed = false];
repeated sint64 unpacked_sint64 = 94 [packed = false];
repeated fixed32 unpacked_fixed32 = 95 [packed = false];
repeated fixed64 unpacked_fixed64 = 96 [packed = false];
repeated sfixed32 unpacked_sfixed32 = 97 [packed = false];
repeated sfixed64 unpacked_sfixed64 = 98 [packed = false];
repeated float unpacked_float = 99 [packed = false];
repeated double unpacked_double = 100 [packed = false];
repeated bool unpacked_bool = 101 [packed = false];
repeated NestedEnum unpacked_nested_enum = 102 [packed = false];
// Map
map<int32, int32> map_int32_int32 = 56;
map<int64, int64> map_int64_int64 = 57;
map<uint32, uint32> map_uint32_uint32 = 58;
map<uint64, uint64> map_uint64_uint64 = 59;
map<sint32, sint32> map_sint32_sint32 = 60;
map<sint64, sint64> map_sint64_sint64 = 61;
map<fixed32, fixed32> map_fixed32_fixed32 = 62;
map<fixed64, fixed64> map_fixed64_fixed64 = 63;
map<sfixed32, sfixed32> map_sfixed32_sfixed32 = 64;
map<sfixed64, sfixed64> map_sfixed64_sfixed64 = 65;
map<int32, float> map_int32_float = 66;
map<int32, double> map_int32_double = 67;
map<bool, bool> map_bool_bool = 68;
map<string, string> map_string_string = 69;
map<string, bytes> map_string_bytes = 70;
map<string, NestedMessage> map_string_nested_message = 71;
map<string, ForeignMessage> map_string_foreign_message = 72;
map<string, NestedEnum> map_string_nested_enum = 73;
map<string, ForeignEnum> map_string_foreign_enum = 74;
oneof oneof_field {
uint32 oneof_uint32 = 111;
NestedMessage oneof_nested_message = 112;
string oneof_string = 113;
bytes oneof_bytes = 114;
bool oneof_bool = 115;
uint64 oneof_uint64 = 116;
float oneof_float = 117;
double oneof_double = 118;
NestedEnum oneof_enum = 119;
google.protobuf.NullValue oneof_null_value = 120;
}
// Well-known types
google.protobuf.BoolValue optional_bool_wrapper = 201;
google.protobuf.Int32Value optional_int32_wrapper = 202;
google.protobuf.Int64Value optional_int64_wrapper = 203;
google.protobuf.UInt32Value optional_uint32_wrapper = 204;
google.protobuf.UInt64Value optional_uint64_wrapper = 205;
google.protobuf.FloatValue optional_float_wrapper = 206;
google.protobuf.DoubleValue optional_double_wrapper = 207;
google.protobuf.StringValue optional_string_wrapper = 208;
google.protobuf.BytesValue optional_bytes_wrapper = 209;
repeated google.protobuf.BoolValue repeated_bool_wrapper = 211;
repeated google.protobuf.Int32Value repeated_int32_wrapper = 212;
repeated google.protobuf.Int64Value repeated_int64_wrapper = 213;
repeated google.protobuf.UInt32Value repeated_uint32_wrapper = 214;
repeated google.protobuf.UInt64Value repeated_uint64_wrapper = 215;
repeated google.protobuf.FloatValue repeated_float_wrapper = 216;
repeated google.protobuf.DoubleValue repeated_double_wrapper = 217;
repeated google.protobuf.StringValue repeated_string_wrapper = 218;
repeated google.protobuf.BytesValue repeated_bytes_wrapper = 219;
google.protobuf.Duration optional_duration = 301;
google.protobuf.Timestamp optional_timestamp = 302;
google.protobuf.FieldMask optional_field_mask = 303;
google.protobuf.Struct optional_struct = 304;
google.protobuf.Any optional_any = 305;
google.protobuf.Value optional_value = 306;
google.protobuf.NullValue optional_null_value = 307;
repeated google.protobuf.Duration repeated_duration = 311;
repeated google.protobuf.Timestamp repeated_timestamp = 312;
repeated google.protobuf.FieldMask repeated_fieldmask = 313;
repeated google.protobuf.Struct repeated_struct = 324;
repeated google.protobuf.Any repeated_any = 315;
repeated google.protobuf.Value repeated_value = 316;
repeated google.protobuf.ListValue repeated_list_value = 317;
// Test field-name-to-JSON-name convention.
// (protobuf says names can be any valid C/C++ identifier.)
int32 fieldname1 = 401;
int32 field_name2 = 402;
int32 _field_name3 = 403;
int32 field__name4_ = 404;
int32 field0name5 = 405;
int32 field_0_name6 = 406;
int32 fieldName7 = 407;
int32 FieldName8 = 408;
int32 field_Name9 = 409;
int32 Field_Name10 = 410;
int32 FIELD_NAME11 = 411;
int32 FIELD_name12 = 412;
int32 __field_name13 = 413;
int32 __Field_name14 = 414;
int32 field__name15 = 415;
int32 field__Name16 = 416;
int32 field_name17__ = 417;
int32 Field_name18__ = 418;
// Reserved for testing unknown fields
reserved 501 to 510;
}
message ForeignMessage {
int32 c = 1;
}
enum ForeignEnum {
FOREIGN_FOO = 0;
FOREIGN_BAR = 1;
FOREIGN_BAZ = 2;
}
message NullHypothesisProto3 {}
message EnumOnlyProto3 {
enum Bool {
kFalse = 0;
kTrue = 1;
}
}

View File

@ -1,144 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/timestamppb";
option java_package = "com.google.protobuf";
option java_outer_classname = "TimestampProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// A Timestamp represents a point in time independent of any time zone or local
// calendar, encoded as a count of seconds and fractions of seconds at
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
// January 1, 1970, in the proleptic Gregorian calendar which extends the
// Gregorian calendar backwards to year one.
//
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
// second table is needed for interpretation, using a [24-hour linear
// smear](https://developers.google.com/time/smear).
//
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
// restricting to that range, we ensure that we can convert to and from [RFC
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
//
// # Examples
//
// Example 1: Compute Timestamp from POSIX `time()`.
//
// Timestamp timestamp;
// timestamp.set_seconds(time(NULL));
// timestamp.set_nanos(0);
//
// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
//
// struct timeval tv;
// gettimeofday(&tv, NULL);
//
// Timestamp timestamp;
// timestamp.set_seconds(tv.tv_sec);
// timestamp.set_nanos(tv.tv_usec * 1000);
//
// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
//
// FILETIME ft;
// GetSystemTimeAsFileTime(&ft);
// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
//
// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
// Timestamp timestamp;
// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
//
// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
//
// long millis = System.currentTimeMillis();
//
// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
// .setNanos((int) ((millis % 1000) * 1000000)).build();
//
// Example 5: Compute Timestamp from Java `Instant.now()`.
//
// Instant now = Instant.now();
//
// Timestamp timestamp =
// Timestamp.newBuilder().setSeconds(now.getEpochSecond())
// .setNanos(now.getNano()).build();
//
// Example 6: Compute Timestamp from current time in Python.
//
// timestamp = Timestamp()
// timestamp.GetCurrentTime()
//
// # JSON Mapping
//
// In JSON format, the Timestamp type is encoded as a string in the
// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
// where {year} is always expressed using four digits while {month}, {day},
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
// is required. A proto3 JSON serializer should always use UTC (as indicated by
// "Z") when printing the Timestamp type and a proto3 JSON parser should be
// able to accept both UTC and other timezones (as indicated by an offset).
//
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
// 01:30 UTC on January 15, 2017.
//
// In JavaScript, one can convert a Date object to this format using the
// standard
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
// method. In Python, a standard `datetime.datetime` object can be converted
// to this format using
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
// ) to obtain a formatter capable of generating timestamps in this format.
//
message Timestamp {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
int64 seconds = 1;
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive.
int32 nanos = 2;
}

View File

@ -1,187 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
import "google/protobuf/any.proto";
import "google/protobuf/source_context.proto";
option cc_enable_arenas = true;
option java_package = "com.google.protobuf";
option java_outer_classname = "TypeProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/typepb";
// A protocol buffer message type.
message Type {
// The fully qualified message name.
string name = 1;
// The list of fields.
repeated Field fields = 2;
// The list of types appearing in `oneof` definitions in this type.
repeated string oneofs = 3;
// The protocol buffer options.
repeated Option options = 4;
// The source context.
SourceContext source_context = 5;
// The source syntax.
Syntax syntax = 6;
}
// A single field of a message type.
message Field {
// Basic field types.
enum Kind {
// Field type unknown.
TYPE_UNKNOWN = 0;
// Field type double.
TYPE_DOUBLE = 1;
// Field type float.
TYPE_FLOAT = 2;
// Field type int64.
TYPE_INT64 = 3;
// Field type uint64.
TYPE_UINT64 = 4;
// Field type int32.
TYPE_INT32 = 5;
// Field type fixed64.
TYPE_FIXED64 = 6;
// Field type fixed32.
TYPE_FIXED32 = 7;
// Field type bool.
TYPE_BOOL = 8;
// Field type string.
TYPE_STRING = 9;
// Field type group. Proto2 syntax only, and deprecated.
TYPE_GROUP = 10;
// Field type message.
TYPE_MESSAGE = 11;
// Field type bytes.
TYPE_BYTES = 12;
// Field type uint32.
TYPE_UINT32 = 13;
// Field type enum.
TYPE_ENUM = 14;
// Field type sfixed32.
TYPE_SFIXED32 = 15;
// Field type sfixed64.
TYPE_SFIXED64 = 16;
// Field type sint32.
TYPE_SINT32 = 17;
// Field type sint64.
TYPE_SINT64 = 18;
}
// Whether a field is optional, required, or repeated.
enum Cardinality {
// For fields with unknown cardinality.
CARDINALITY_UNKNOWN = 0;
// For optional fields.
CARDINALITY_OPTIONAL = 1;
// For required fields. Proto2 syntax only.
CARDINALITY_REQUIRED = 2;
// For repeated fields.
CARDINALITY_REPEATED = 3;
}
// The field type.
Kind kind = 1;
// The field cardinality.
Cardinality cardinality = 2;
// The field number.
int32 number = 3;
// The field name.
string name = 4;
// The field type URL, without the scheme, for message or enumeration
// types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
string type_url = 6;
// The index of the field type in `Type.oneofs`, for message or enumeration
// types. The first type has index 1; zero means the type is not in the list.
int32 oneof_index = 7;
// Whether to use alternative packed wire representation.
bool packed = 8;
// The protocol buffer options.
repeated Option options = 9;
// The field JSON name.
string json_name = 10;
// The string value of the default value of this field. Proto2 syntax only.
string default_value = 11;
}
// Enum type definition.
message Enum {
// Enum type name.
string name = 1;
// Enum value definitions.
repeated EnumValue enumvalue = 2;
// Protocol buffer options.
repeated Option options = 3;
// The source context.
SourceContext source_context = 4;
// The source syntax.
Syntax syntax = 5;
}
// Enum value definition.
message EnumValue {
// Enum value name.
string name = 1;
// Enum value number.
int32 number = 2;
// Protocol buffer options.
repeated Option options = 3;
}
// A protocol buffer option, which can be attached to a message, field,
// enumeration, etc.
message Option {
// The option's name. For protobuf built-in options (options defined in
// descriptor.proto), this is the short name. For example, `"map_entry"`.
// For custom options, it should be the fully-qualified name. For example,
// `"google.api.http"`.
string name = 1;
// The option's value packed in an Any message. If the value is a primitive,
// the corresponding wrapper type defined in google/protobuf/wrappers.proto
// should be used. If the value is an enum, it should be stored as an int32
// value using the google.protobuf.Int32Value type.
Any value = 2;
}
// The syntax in which a protocol buffer element is defined.
enum Syntax {
// Syntax `proto2`.
SYNTAX_PROTO2 = 0;
// Syntax `proto3`.
SYNTAX_PROTO3 = 1;
}

View File

@ -1,123 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Wrappers for primitive (non-message) types. These types are useful
// for embedding primitives in the `google.protobuf.Any` type and for places
// where we need to distinguish between the absence of a primitive
// typed field and its default value.
//
// These wrappers have no meaningful use within repeated fields as they lack
// the ability to detect presence on individual elements.
// These wrappers have no meaningful use within a map or a oneof since
// individual entries of a map or fields of a oneof can already detect presence.
syntax = "proto3";
package google.protobuf;
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/wrapperspb";
option java_package = "com.google.protobuf";
option java_outer_classname = "WrappersProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
// Wrapper message for `double`.
//
// The JSON representation for `DoubleValue` is JSON number.
message DoubleValue {
// The double value.
double value = 1;
}
// Wrapper message for `float`.
//
// The JSON representation for `FloatValue` is JSON number.
message FloatValue {
// The float value.
float value = 1;
}
// Wrapper message for `int64`.
//
// The JSON representation for `Int64Value` is JSON string.
message Int64Value {
// The int64 value.
int64 value = 1;
}
// Wrapper message for `uint64`.
//
// The JSON representation for `UInt64Value` is JSON string.
message UInt64Value {
// The uint64 value.
uint64 value = 1;
}
// Wrapper message for `int32`.
//
// The JSON representation for `Int32Value` is JSON number.
message Int32Value {
// The int32 value.
int32 value = 1;
}
// Wrapper message for `uint32`.
//
// The JSON representation for `UInt32Value` is JSON number.
message UInt32Value {
// The uint32 value.
uint32 value = 1;
}
// Wrapper message for `bool`.
//
// The JSON representation for `BoolValue` is JSON `true` and `false`.
message BoolValue {
// The bool value.
bool value = 1;
}
// Wrapper message for `string`.
//
// The JSON representation for `StringValue` is JSON string.
message StringValue {
// The string value.
string value = 1;
}
// Wrapper message for `bytes`.
//
// The JSON representation for `BytesValue` is JSON string.
message BytesValue {
// The bytes value.
bytes value = 1;
}

View File

@ -4,11 +4,11 @@ if("${yaLanTingLibs_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
else() else()
# else find installed yalantinglibs # else find installed yalantinglibs
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(coro_rpc_examples) project(struct_pb_examples)
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release") set(CMAKE_BUILD_TYPE "Release")
endif() endif()
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
# if you have install ylt # if you have install ylt
@ -17,11 +17,8 @@ else()
# else # else
# include_directories(include) # include_directories(include)
# include_directories(include/ylt/thirdparty) # include_directories(include/ylt/thirdparty)
include(struct_pb_helper.cmake)
endif() endif()
find_package(Protobuf QUIET) add_executable(struct_pb_examples
if (Protobuf_FOUND) main.cpp
add_executable(struct_pb_tutorial tutorial.cpp) )
target_protos_struct_pb(struct_pb_tutorial PRIVATE addressbook.proto)
endif()

View File

@ -1,26 +0,0 @@
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}

View File

@ -1,50 +0,0 @@
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
enum Corpus {
CORPUS_UNSPECIFIED = 0;
CORPUS_UNIVERSAL = 1;
CORPUS_WEB = 2;
CORPUS_IMAGES = 3;
CORPUS_LOCAL = 4;
CORPUS_NEWS = 5;
CORPUS_PRODUCTS = 6;
CORPUS_VIDEO = 7;
}
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
int64 ival = 1;
bool booly = 2;
}
}
message MiddleBB { // Level 1
message Inner { // Level 2
int32 ival = 1;
bool booly = 2;
}
}
}
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
message SubMessage {
int32 val = 1;
}
message SampleMap {
map<string, Project> projects = 3;
}
message Project {
string name = 1;
}

View File

@ -0,0 +1,68 @@
#include <cassert>
#include <iostream>
#include <ylt/struct_pb.hpp>
struct my_struct : struct_pb::base_impl<my_struct> {
my_struct() = default;
my_struct(int a, bool b, struct_pb::fixed64_t c) : x(a), y(b), z(c) {}
int x;
bool y;
struct_pb::fixed64_t z;
};
REFLECTION(my_struct, x, y, z);
struct nest : struct_pb::base_impl<nest> {
nest() = default;
nest(std::string s, my_struct t, int v)
: name(std::move(s)), value(t), var(v) {}
std::string name;
my_struct value;
int var;
};
REFLECTION(nest, name, value, var);
struct person {
int id;
std::string name;
int age;
};
REFLECTION(person, id, name, age);
int main() {
nest v{"Hi", my_struct{1, false, {3}}, 5};
std::string s;
struct_pb::to_pb(v, s);
nest v2;
struct_pb::from_pb(v2, s);
assert(v.var == v2.var);
assert(v.value.y == v2.value.y);
assert(v.value.z == v2.value.z);
person p{1, "tom", 22};
std::string str;
struct_pb::to_pb(p, str);
person p1;
struct_pb::from_pb(p1, str);
assert(p.age == p1.age);
assert(p.name == p1.name);
assert(p.id == p1.id);
// dynamic reflection
auto t = struct_pb::create_instance("nest");
auto names = t->get_fields_name();
bool r = (names == std::vector<std::string_view>{"name", "value", "var"});
assert(r);
t->set_field_value("name", std::string("tom"));
auto name = t->get_field_value<std::string>("name");
assert(name == "tom");
auto d = dynamic_cast<nest *>(t.get());
assert(d->name == "tom");
t->set_field_value<std::string>("name", "hello");
auto &field_name = t->get_field_value<std::string>("name");
assert(field_name == "hello");
}

View File

@ -1,104 +0,0 @@
#include <fstream>
#include <iostream>
#include <string>
#include "addressbook.struct_pb.h"
void prompt_for_address(tutorial::Person& person) {
std::cout << "==================================" << std::endl;
std::cout << " Add People " << std::endl;
std::cout << "==================================" << std::endl;
std::cout << "Enter person ID number: ";
std::cin >> person.id;
std::cin.ignore(256, '\n');
std::cout << "Enter name: ";
std::getline(std::cin, person.name);
std::cout << "Enter email address (blank for none): ";
std::getline(std::cin, person.email);
while (true) {
std::cout << "Enter a phone number (or leave blank to finish): ";
tutorial::Person::PhoneNumber phone_number;
std::getline(std::cin, phone_number.number);
if (phone_number.number.empty()) {
break;
}
std::cout << "Is this a mobile, home, or work phone? ";
std::string type;
std::getline(std::cin, type);
if (type == "mobile") {
phone_number.type = tutorial::Person::PhoneType::MOBILE;
}
else if (type == "home") {
phone_number.type = tutorial::Person::PhoneType::HOME;
}
else if (type == "work") {
phone_number.type = tutorial::Person::PhoneType::WORK;
}
else {
std::cout << "Unknown phone type: Using default." << std::endl;
}
person.phones.push_back(phone_number);
}
}
void list_people(const tutorial::AddressBook& address_book) {
std::cout << "==================================" << std::endl;
std::cout << " List People " << std::endl;
std::cout << "==================================" << std::endl;
for (const auto& person : address_book.people) {
std::cout << " Person ID: " << person.id << std::endl;
std::cout << " Name: " << person.name << std::endl;
if (!person.email.empty()) {
std::cout << "E-mail address: " << person.email << std::endl;
}
for (const auto& phone : person.phones) {
switch (phone.type) {
case tutorial::Person::PhoneType::MOBILE:
std::cout << "Mobile phone #: ";
break;
case tutorial::Person::PhoneType::HOME:
std::cout << " Home phone #: ";
break;
case tutorial::Person::PhoneType::WORK:
std::cout << " Work phone #: ";
break;
}
std::cout << phone.number << std::endl;
}
}
}
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << std::endl;
return -1;
}
tutorial::AddressBook address_book;
std::fstream input(argv[1], std::ios::in | std::ios::binary);
if (!input) {
std::cout << argv[1] << ": File not found. Creating a new file."
<< std::endl;
}
else {
input.seekg(0, input.end);
int length = input.tellg();
input.seekg(0, input.beg);
std::string buffer;
buffer.resize(length);
input.read(buffer.data(), buffer.size());
input.close();
bool ok = struct_pb::deserialize_to(address_book, buffer);
if (!ok) {
std::cerr << "Failed to parse address book." << std::endl;
return -1;
}
}
list_people(address_book);
address_book.people.emplace_back();
prompt_for_address(address_book.people.back());
std::fstream output(argv[1],
std::ios::out | std::ios::trunc | std::ios::binary);
std::string buffer = struct_pb::serialize<std::string>(address_book);
output.write(buffer.data(), buffer.size());
output.close();
list_people(address_book);
std::cout << "Done!!!" << std::endl;
return 0;
}

View File

@ -1,18 +0,0 @@
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/bin)
find_package(Protobuf QUIET)
if (Protobuf_FOUND)
file(GLOB SRCS "*.cpp" "*.h")
add_executable(protoc-gen-struct_pb ${SRCS})
target_link_libraries(protoc-gen-struct_pb PUBLIC protobuf::libprotoc protobuf::libprotobuf)
install(
TARGETS protoc-gen-struct_pb
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
)
else ()
message(STATUS "struct_pb is skipped. To build struct_pb protoc plugin, you must install libprotoc first\n"
"see https://alibaba.github.io/yalantinglibs/en/struct_pb/struct_pb_generating_your_struct.html"
)
endif ()

View File

@ -1,328 +0,0 @@
#include "EnumFieldGenerator.h"
#include "helpers.hpp"
namespace struct_pb {
namespace compiler {
EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor *descriptor,
const Options &options)
: FieldGenerator(descriptor, options) {}
std::string EnumFieldGenerator::cpp_type_name() const {
auto name = qualified_enum_name(d_->enum_type(), options_);
if (is_optional()) {
return "std::optional<" + name + ">";
}
return name;
}
void EnumFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
if (is_optional()) {
format("if ($1$.has_value()) {\n", value);
format.indent();
format("total += $1$ + ", calculate_tag_size(d_));
generate_calculate_size_only(p, value + ".value()");
format(";\n");
format.outdent();
format("}\n");
}
else {
if (can_ignore_default_value) {
p->Print(
{
{"tag_sz", calculate_tag_size(d_)},
{"value", value},
{"default_value", default_value()},
},
R"(
if ($value$ != $default_value$) {
total += $tag_sz$ + )");
generate_calculate_size_only(p, value);
p->Print(R"(;
}
)");
}
else {
p->Print(
{
{"tag_sz", calculate_tag_size(d_)},
},
R"(
total += $tag_sz$ + )");
generate_calculate_size_only(p, value);
p->Print(R"(;
}
)");
}
}
}
void EnumFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
// auto v = p->WithVars({{"tag", calculate_tag(d_)}, {"value", value}});
if (is_optional()) {
p->Print({{"tag", calculate_tag_str(d_)}, {"value", value}}, R"(
if ($value$.has_value()) {
)");
generate_serialization_only(p, value + ".value()");
p->Print(R"(
}
)");
}
else {
if (can_ignore_default_value) {
p->Print({{"tag", calculate_tag_str(d_)},
{"value", value},
{"default_value", default_value()}},
R"(
if ($value$ != $default_value$) {
)");
generate_serialization_only(p, value);
p->Print(R"(
}
)");
}
else {
generate_serialization_only(p, value);
}
}
}
void EnumFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print({{"tag", calculate_tag_str(d_)}, {"value", value}}, R"(
serialize_varint(data, pos, size, $tag$);
serialize_varint(data, pos, size, static_cast<uint64_t>($value$));
)");
}
void EnumFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print(
{
{"tag", calculate_tag_str(d_)},
{"value", value},
},
R"(
case $tag$: {
)");
generate_deserialization_only(p, value);
p->Print(R"(
break;
}
)");
}
void EnumFieldGenerator::generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &output,
const std::string &max_size) const {
auto enum_name = qualified_enum_name(d_->enum_type(), options_);
p->Print(
{{"enum_name", enum_name}, {"output", output}, {"max_size", max_size}},
R"(
uint64_t enum_val_tmp{};
ok = deserialize_varint(data, pos, $max_size$, enum_val_tmp);
if (!ok) {
return false;
}
$output$ = static_cast<$enum_name$>(enum_val_tmp);
)");
}
RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
const FieldDescriptor *descriptor, const Options &options)
: FieldGenerator(descriptor, options) {}
std::string RepeatedEnumFieldGenerator::cpp_type_name() const {
return "std::vector<" + qualified_enum_name(d_->enum_type(), options_) + ">";
}
void RepeatedEnumFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
if (can_ignore_default_value) {
if (is_packed()) {
p->Print({{"value", value}}, R"(
if (!$value$.empty()) { // packed
)");
generate_calculate_size_only(p, value, "sz");
p->Print(
{
{"tag_sz", calculate_tag_size(d_)},
},
R"(
total += $tag_sz$ + calculate_varint_size(sz) + sz;
}
)");
}
else {
p->Print({{"value", value}}, R"(
if (!$value$.empty()) { // unpacked
)");
generate_calculate_size_only(p, value, "sz");
p->Print(
{
{"tag_sz", calculate_tag_size(d_)},
},
R"(
total += sz;
}
)");
}
}
else {
if (is_packed()) {
generate_calculate_size_only(p, value, "sz");
p->Print(
{
{"tag_sz", calculate_tag_size(d_)},
},
R"(
// packed
total += $tag_sz$ + calculate_varint_size(sz) + sz;
)");
}
else {
generate_calculate_size_only(p, value, "sz");
p->Print(
R"(
// unpacked
total += sz;
)");
}
}
}
void RepeatedEnumFieldGenerator::generate_calculate_size_only(
google::protobuf::io::Printer *p, const std::string &value,
const std::string &output) const {
if (is_packed()) {
p->Print({{"value", value},
{"output", output},
{"tag_sz", calculate_tag_size(d_)}},
R"(
std::size_t $output$ = 0;
for(const auto& v: $value$) {
$output$ += calculate_varint_size(static_cast<uint64_t>(v));
}
)");
}
else {
p->Print({{"value", value},
{"output", output},
{"tag_sz", calculate_tag_size(d_, true)}},
R"(
std::size_t $output$ = 0;
for(const auto& v: $value$) {
$output$ += $tag_sz$;
$output$ += calculate_varint_size(static_cast<uint64_t>(v));
}
)");
}
}
void RepeatedEnumFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
if (can_ignore_default_value) {
p->Print({{"value", value}}, R"(
if (!$value$.empty()) {
)");
generate_serialization_only(p, value);
p->Print("}\n");
}
else {
generate_serialization_only(p, value);
}
}
void RepeatedEnumFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
if (is_packed()) {
p->Print({{"tag", std::to_string(calculate_tag(d_))}}, R"(
serialize_varint(data, pos, size, $tag$);
)");
generate_calculate_size_only(p, value, "sz");
p->Print({{"value", value}},
R"(
serialize_varint(data, pos, size, sz);
for(const auto& v: $value$) {
serialize_varint(data, pos, size, static_cast<uint64_t>(v));
}
)");
}
else {
p->Print(
{{"tag", std::to_string(calculate_tag(d_, true))}, {"value", value}},
R"(
for(const auto& v: $value$) {
serialize_varint(data, pos, size, $tag$);
serialize_varint(data, pos, size, static_cast<uint64_t>(v));
}
)");
}
}
void RepeatedEnumFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
// generate packed
p->Print({{"tag", packed_tag()},
{"value", value},
{"enum_name", qualified_enum_name(d_->enum_type(), options_)}
},
R"(
case $tag$: {
uint64_t sz = 0;
ok = deserialize_varint(data, pos, size, sz);
if (!ok) {
return false;
}
std::size_t cur_max_sz = pos + sz;
if (cur_max_sz > size) {
return false;
}
while (pos < cur_max_sz) {
uint64_t enum_val_tmp{};
ok = deserialize_varint(data, pos, cur_max_sz, enum_val_tmp);
if (!ok) {
return false;
}
$value$.push_back(static_cast<$enum_name$>(enum_val_tmp));
}
break;
}
)");
p->Print({{"tag", unpacked_tag()},
{"value", value},
{"enum_name", qualified_enum_name(d_->enum_type(), options_)}},
R"(
case $tag$: {
uint64_t enum_val_tmp{};
ok = deserialize_varint(data, pos, size, enum_val_tmp);
if (!ok) {
return false;
}
$value$.push_back(static_cast<$enum_name$>(enum_val_tmp));
break;
}
)");
}
bool RepeatedEnumFieldGenerator::is_packed() const {
return d_->is_packable() && d_->is_packed();
}
std::string RepeatedEnumFieldGenerator::packed_tag() const {
uint32_t number = d_->number();
uint32_t wire_type = 2;
auto tag = number << 3 | wire_type;
return std::to_string(tag);
}
std::string RepeatedEnumFieldGenerator::unpacked_tag() const {
uint32_t number = d_->number();
uint32_t wire_type = 0;
auto tag = number << 3 | wire_type;
return std::to_string(tag);
}
std::string EnumFieldGenerator::default_value() const {
return qualified_enum_name(d_->enum_type(), options_) +
"::" + d_->default_value_enum()->name();
}
void EnumFieldGenerator::generate_calculate_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print({{"value", value}},
"calculate_varint_size(static_cast<uint64_t>($value$))");
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,56 +0,0 @@
#pragma once
#include "FieldGenerator.h"
namespace struct_pb {
namespace compiler {
class EnumFieldGenerator : public FieldGenerator {
public:
EnumFieldGenerator(const FieldDescriptor *descriptor, const Options &options);
std::string cpp_type_name() const override;
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_size_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
void generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &output,
const std::string &max_size = "size") const;
private:
std::string default_value() const;
};
class RepeatedEnumFieldGenerator : public FieldGenerator {
public:
RepeatedEnumFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
std::string cpp_type_name() const override;
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_size_only(google::protobuf::io::Printer *p,
const std::string &value,
const std::string &output) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
private:
bool is_packed() const;
std::string packed_tag() const;
std::string unpacked_tag() const;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,37 +0,0 @@
#include "EnumGenerator.h"
#include "helpers.hpp"
namespace struct_pb {
namespace compiler {
void EnumGenerator::generate(google::protobuf::io::Printer *p) {
std::string enum_name = resolve_keyword(d_->name());
p->Print({{"enum_name", enum_name}}, R"(
enum class $enum_name$ {
)");
for (int j = 0; j < d_->value_count(); ++j) {
auto v = d_->value(j);
p->Print({{"name", resolve_keyword(v->name())},
{"value", std::to_string(v->number())}},
R"(
$name$ = $value$,
)");
}
p->Print(
R"(
};
)");
}
void EnumGenerator::generate_definition(google::protobuf::io::Printer *p) {
Formatter format(p);
format("enum class $1$: int {\n", resolve_keyword(d_->name()));
format.indent();
for (int i = 0; i < d_->value_count(); ++i) {
auto value = resolve_keyword(d_->value(i)->name());
auto number = d_->value(i)->number();
format("$1$ = $2$,\n", value, number);
}
format.outdent();
format("};\n");
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,20 +0,0 @@
#pragma once
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include "GeneratorBase.h"
#include "google/protobuf/io/printer.h"
namespace struct_pb {
namespace compiler {
class EnumGenerator : public GeneratorBase {
public:
EnumGenerator(const google::protobuf::EnumDescriptor *d, Options options)
: GeneratorBase(options), d_(d) {}
void generate(google::protobuf::io::Printer *p);
void generate_definition(google::protobuf::io::Printer *p);
private:
const google::protobuf::EnumDescriptor *d_;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,557 +0,0 @@
#include "FieldGenerator.h"
#include "EnumFieldGenerator.h"
#include "MapFieldGenerator.h"
#include "MessageFieldGenerator.h"
#include "OneofFieldGenerator.h"
#include "PrimitiveFieldGenerator.h"
#include "StringFieldGenerator.h"
#include "google/protobuf/io/printer.h"
#include "helpers.hpp"
using namespace google::protobuf;
namespace struct_pb {
namespace compiler {
bool is_varint(const FieldDescriptor *f) {
switch (f->type()) {
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_UINT32:
case FieldDescriptor::TYPE_BOOL:
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SINT64:
return true;
default:
return false;
}
}
bool is_sint(const FieldDescriptor *f) {
switch (f->type()) {
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SINT64:
return true;
default:
return false;
}
}
bool is_sint32(const FieldDescriptor *f) {
switch (f->type()) {
case FieldDescriptor::TYPE_SINT32:
return true;
default:
return false;
}
}
bool is_enum(const FieldDescriptor *f) {
return f->type() == FieldDescriptor::TYPE_ENUM;
}
bool is_bool(const FieldDescriptor *f) {
return f->type() == FieldDescriptor::TYPE_BOOL;
}
bool is_i64(const FieldDescriptor *f) {
switch (f->type()) {
case FieldDescriptor::TYPE_FIXED64:
case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_DOUBLE:
return true;
default:
return false;
}
}
bool is_i32(const FieldDescriptor *f) {
switch (f->type()) {
case FieldDescriptor::TYPE_FIXED32:
case FieldDescriptor::TYPE_SFIXED32:
case FieldDescriptor::TYPE_FLOAT:
return true;
default:
return false;
}
}
bool is_string(const FieldDescriptor *f) {
switch (f->type()) {
case FieldDescriptor::TYPE_STRING:
case FieldDescriptor::TYPE_BYTES:
return true;
default:
return false;
}
}
bool is_message(const FieldDescriptor *f) {
return f->type() == FieldDescriptor::TYPE_MESSAGE;
}
bool is_group(const FieldDescriptor *f) {
return f->type() == FieldDescriptor::TYPE_GROUP;
}
bool is_number(const FieldDescriptor *f) {
switch (f->type()) {
case FieldDescriptor::TYPE_DOUBLE:
case FieldDescriptor::TYPE_FLOAT:
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_FIXED64:
case FieldDescriptor::TYPE_FIXED32:
case FieldDescriptor::TYPE_BOOL:
case FieldDescriptor::TYPE_UINT32:
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_SFIXED32:
case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SINT64:
return true;
default:
return false;
}
}
uint32_t calculate_tag(const FieldDescriptor *f, bool ignore_repeated) {
uint32_t number = f->number();
uint32_t wire_type;
if (f->is_repeated() && !ignore_repeated) {
wire_type = 2;
}
else if (is_varint(f)) {
wire_type = 0;
}
else if (is_i64(f)) {
wire_type = 1;
}
else if (is_i32(f)) {
wire_type = 5;
}
else {
wire_type = 2;
}
return (number << 3) | wire_type;
}
std::string calculate_tag_str(const FieldDescriptor *f, bool ignore_repeated) {
return std::to_string(calculate_tag(f, ignore_repeated));
}
std::size_t calculate_varint_size(uint64_t t) {
std::size_t ret = 0;
do {
ret++;
t >>= 7;
} while (t != 0);
return ret;
}
std::string calculate_tag_size(const FieldDescriptor *f, bool ignore_repeated) {
auto tag = calculate_tag(f, ignore_repeated);
auto tag_size = calculate_varint_size(tag);
return std::to_string(tag_size);
}
std::string get_comment(const FieldDescriptor *d_) {
std::string comment;
if (d_->has_optional_keyword()) {
comment += "optional ";
}
if (d_->is_repeated()) {
comment += "repeated ";
}
if (is_message(d_)) {
comment += class_name(d_->message_type());
}
else {
comment += d_->type_name();
}
comment += " ";
comment += d_->name();
comment += " = ";
comment += std::to_string(d_->number());
comment += ", tag = ";
comment += std::to_string(calculate_tag(d_));
return comment;
}
void FieldGenerator::generate_definition(
google::protobuf::io::Printer *p) const {
p->Print(
{
{"type", cpp_type_name()},
{"name", name()},
{"pb_type", pb_type_name()},
{"number", std::to_string(d_->number())},
},
R"($type$ $name$; // $pb_type$, field number = $number$
)");
}
std::string FieldGenerator::get_type_name() const {
switch (d_->type()) {
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return "double";
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
return "float";
case google::protobuf::FieldDescriptor::TYPE_INT64:
case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
case google::protobuf::FieldDescriptor::TYPE_SINT64:
return "int64_t";
case google::protobuf::FieldDescriptor::TYPE_UINT64:
case google::protobuf::FieldDescriptor::TYPE_FIXED64:
return "uint64_t";
case google::protobuf::FieldDescriptor::TYPE_UINT32:
case google::protobuf::FieldDescriptor::TYPE_FIXED32:
return "uint32_t";
case google::protobuf::FieldDescriptor::TYPE_INT32:
case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
case google::protobuf::FieldDescriptor::TYPE_SINT32:
return "int32_t";
case google::protobuf::FieldDescriptor::TYPE_BOOL:
return "bool";
case google::protobuf::FieldDescriptor::TYPE_STRING:
case google::protobuf::FieldDescriptor::TYPE_BYTES:
return "std::string";
case google::protobuf::FieldDescriptor::TYPE_MESSAGE: {
auto m = d_->message_type();
if (d_->is_map()) {
auto key = m->field(0);
auto value = m->field(1);
std::string type_name = "std::map<";
type_name += FieldGenerator(key, options_).get_type_name();
type_name += ", ";
type_name += FieldGenerator(value, options_).get_type_name();
type_name += ">";
return type_name;
}
else if (d_->is_repeated()) {
if (is_message(d_)) {
return qualified_class_name(d_->message_type(), options_);
}
else {
return m->name();
}
}
else {
auto p = d_->containing_type();
if (is_map_entry(p)) {
return m->name();
}
else {
return "std::unique_ptr<" +
qualified_class_name(d_->message_type(), options_) + ">";
}
}
}
case google::protobuf::FieldDescriptor::TYPE_ENUM:
return d_->enum_type()->name();
case google::protobuf::FieldDescriptor::TYPE_GROUP:
// workaround for group
return "std::string";
default: {
return "not support now";
}
}
}
void FieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
// auto name = value.empty() ? "t." + this->name() : value;
// auto tag = calculate_tag(d_);
// auto v =
// p->WithVars({{"name", name}, {"tag_size",
// calculate_varint_size(tag)}});
// std::string comment = get_comment(d_);
// p->Print({{"comment", comment}}, R"(
//// $comment$
//)");
// if (d_->has_optional_keyword() && !is_message(d_)) {
// p->Print(R"(
// if ($name$.has_value()) {
//)");
// generate_calculate_one(p, name + ".value()", false);
// p->Print("}\n");
// } else if (d_->is_repeated()) {
// generate_calculate_vector(p);
// } else {
// generate_calculate_one(p, name, can_ignore_default_value);
// }
}
void FieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
// auto name = value.empty() ? "t." + this->name() : value;
// auto tag = calculate_tag(d_);
// auto v =
// p->WithVars({{"name", name}, {"tag_size",
// calculate_varint_size(tag)}});
// auto comment = get_comment(d_);
// p->Print({{"comment", comment}}, R"(
//// $comment$
//)");
// if (is_optional()) {
// p->Print(R"(
// if ($name$.has_value()) {
//)");
// generate_serialize_one(p, name + ".value()", false);
// p->Print("}\n");
// } else if (d_->is_repeated()) {
// generate_serialize_vector(p);
// } else {
// generate_serialize_one(p, name, can_ignore_default_value);
// }
}
void FieldGenerator::generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const {}
bool FieldGenerator::is_ptr() const {
auto p = d_->containing_type();
if (p && is_map_entry(p)) {
return false;
}
return true;
}
std::string FieldGenerator::name() const { return resolve_keyword(d_->name()); }
bool FieldGenerator::is_optional() const {
auto p = d_->containing_type();
if (p && is_map_entry(p)) {
return false;
}
// return d_->is_optional();
// return d_->has_optional_keyword() && !is_message(d_);
return d_->has_presence();
}
std::string FieldGenerator::qualified_name() const {
if (is_message(d_)) {
return qualified_class_name(d_->message_type(), options_);
}
else if (is_enum(d_)) {
return qualified_enum_name(d_->enum_type(), options_);
}
else {
return get_type_name();
}
}
std::string FieldGenerator::pb_type_name() const { return d_->type_name(); }
void FieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, bool can_ignore_default_value) const {
auto oneof = d_->real_containing_oneof();
if (oneof) {
generate_calculate_size(p, "t." + resolve_keyword(oneof->name()),
can_ignore_default_value);
}
else {
generate_calculate_size(p, "t." + name(), can_ignore_default_value);
}
}
void FieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, bool can_ignore_default_value) const {
auto oneof = d_->real_containing_oneof();
if (oneof) {
generate_serialization(p, "t." + resolve_keyword(oneof->name()),
can_ignore_default_value);
}
else {
Formatter format(p);
format("// ");
generate_definition(p);
generate_serialization(p, "t." + name(), can_ignore_default_value);
}
}
void FieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p) const {
auto oneof = d_->real_containing_oneof();
if (oneof) {
generate_deserialization(p, "t." + resolve_keyword(oneof->name()));
}
else {
Formatter format(p);
format("// ");
generate_definition(p);
generate_deserialization(p, "t." + name());
}
}
std::string FieldGenerator::cpp_type_name() const {
assert(false && "why?");
return "???";
}
void OneofGenerator::generate_definition(google::protobuf::io::Printer *p) {
p->Print(R"(std::variant<std::monostate
)");
for (int i = 0; i < d_->field_count(); ++i) {
auto f = d_->field(i);
FieldGenerator fg(f, options_);
std::string type_name = fg.get_type_name();
if (is_message(f)) {
type_name = qualified_class_name(f->message_type(), options_);
type_name = "std::unique_ptr<" + type_name + ">";
}
else if (is_enum(f)) {
type_name = qualified_enum_name(f->enum_type(), options_);
}
p->Print(
{
{"type", type_name},
{"name", f->name()},
{"pb_type", f->type_name()},
{"number", std::to_string(f->number())},
},
R"(, $type$ // $pb_type$, field number = $number$
)");
}
p->Print({{"name", name()}}, R"(> $name$;
)");
for (int i = 0; i < d_->field_count(); ++i) {
auto f = d_->field(i);
if (is_message(f)) {
MessageOneofFieldGenerator(f, options_).generate_setter_and_getter(p);
}
else if (is_string(f)) {
StringOneofFieldGenerator(f, options_).generate_setter_and_getter(p);
}
else if (is_enum(f)) {
EnumOneofFieldGenerator(f, options_).generate_setter_and_getter(p);
}
else {
PrimitiveOneofFieldGenerator(f, options_).generate_setter_and_getter(p);
}
}
}
void OneofGenerator::generate_calculate_size(google::protobuf::io::Printer *p) {
Formatter format(p);
format("{\n");
format.indent();
format("switch (t.$1$.index()) {\n", name());
p->Print(R"(
case 0: {
// empty, do nothing
break;
}
)");
for (int i = 0; i < d_->field_count(); ++i) {
auto f = d_->field(i);
format("case $1$: {\n", i + 1);
format.indent();
std::string value =
"std::get<" + std::to_string(i + 1) + ">(t." + name() + ")";
FieldGenerator(f, options_).generate_calculate_size(p, value, false);
format("break;\n");
format.outdent();
format("}\n");
}
format("}\n");
format.outdent();
format("}\n");
}
void OneofGenerator::generate_serialization(
google::protobuf::io::Printer *p,
const google::protobuf::FieldDescriptor *f) {
// auto index = get_index(f);
// assert(index != 0);
// auto v = p->WithVars({{"name", "t." + name()}, {"index", index}});
// Formatter format(p);
// format("if ($name$.index() == $index$) {\n");
// format.indent();
// std::string name = "std::get<";
// name += std::to_string(index);
// name += ">";
// name += "(";
// name += "t." + this->name();
// name += ")";
// FieldGenerator(f, options_).generate_serialization(p, name, false);
// format.outdent();
// format("}\n");
}
void OneofGenerator::generate_deserialization(
google::protobuf::io::Printer *p,
const google::protobuf::FieldDescriptor *f) {
// auto index = get_index(f);
// assert(index != 0);
// auto v = p->WithVars({{"name", "t." + name()}, {"index", index}});
// Formatter format(p);
// FieldGenerator fg(f, options_);
// std::string end =
// "t." + name() + ".emplace<" + std::to_string(index) + ">(tmp);";
// if (is_message(f)) {
// auto type_name = qualified_class_name(f->message_type(), options_);
// fg.generate_deserialization(
// p, "tmp", type_name + "* tmp = new " + type_name + "();", end);
// } else if (is_enum(f)) {
// auto type_name = qualified_enum_name(f->enum_type(), options_);
// fg.generate_deserialization(p, "tmp", type_name + " tmp{};", end);
// } else {
// fg.generate_deserialization(p, "tmp", fg.get_type_name() + " tmp{};",
// end);
// }
}
int OneofGenerator::get_index(
const google::protobuf::FieldDescriptor *f) const {
auto index = 0;
for (int i = 0; i < d_->field_count(); ++i) {
auto fd = d_->field(i);
if (fd == f) {
index = i + 1;
}
}
return index;
}
std::string OneofGenerator::name() const { return resolve_keyword(d_->name()); }
FieldGenerator *FieldGeneratorMap::MakeGenerator(const FieldDescriptor *field,
const Options &options) {
if (field->is_repeated()) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
if (field->is_map()) {
return new MapFieldGenerator(field, options);
}
else {
return new RepeatedMessageFieldGenerator(field, options);
}
case FieldDescriptor::CPPTYPE_STRING:
return new RepeatedStringFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_ENUM:
return new RepeatedEnumFieldGenerator(field, options);
default:
return new RepeatedPrimitiveFieldGenerator(field, options);
}
}
else if (field->real_containing_oneof()) {
return new OneofFieldGenerator(field, options);
// switch (field->cpp_type()) {
// case FieldDescriptor::CPPTYPE_MESSAGE:
// return new MessageOneofFieldGenerator(field, options, scc_analyzer);
// case FieldDescriptor::CPPTYPE_STRING:
// return new StringOneofFieldGenerator(field, options);
// case FieldDescriptor::CPPTYPE_ENUM:
// return new EnumOneofFieldGenerator(field, options);
// default:
// return new PrimitiveOneofFieldGenerator(field, options);
// }
}
else {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
return new MessageFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_STRING:
return new StringFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_ENUM:
return new EnumFieldGenerator(field, options);
default:
return new PrimitiveFieldGenerator(field, options);
}
}
}
FieldGeneratorMap::FieldGeneratorMap(const Descriptor *descriptor,
const Options &options)
: descriptor_(descriptor), field_generators_(descriptor->field_count()) {
// Construct all the FieldGenerators.
for (int i = 0; i < descriptor->field_count(); i++) {
field_generators_[i].reset(MakeGenerator(descriptor->field(i), options));
}
}
const FieldGenerator &FieldGeneratorMap::get(
const FieldDescriptor *field) const {
return *field_generators_[field->index()];
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,90 +0,0 @@
#pragma once
#include "GeneratorBase.h"
#include "helpers.hpp"
namespace struct_pb {
namespace compiler {
class OneofGenerator;
bool is_varint(const FieldDescriptor *f);
bool is_sint(const FieldDescriptor *f);
bool is_sint32(const FieldDescriptor *f);
bool is_bool(const FieldDescriptor *f);
bool is_i64(const FieldDescriptor *f);
bool is_i32(const FieldDescriptor *f);
uint32_t calculate_tag(const FieldDescriptor *f, bool ignore_repeated = false);
std::string calculate_tag_str(const FieldDescriptor *f,
bool ignore_repeated = false);
std::string calculate_tag_size(const FieldDescriptor *f,
bool ignore_repeated = false);
class FieldGenerator : public GeneratorBase {
public:
FieldGenerator(const google::protobuf::FieldDescriptor *d,
const Options &options)
: GeneratorBase(options), d_(d) {}
void generate_definition(google::protobuf::io::Printer *p) const;
void generate_calculate_size(google::protobuf::io::Printer *p,
bool can_ignore_default_value) const;
void generate_serialization(google::protobuf::io::Printer *p,
bool can_ignore_default_value) const;
void generate_deserialization(google::protobuf::io::Printer *p) const;
virtual void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const;
virtual void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const;
virtual void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const;
virtual std::string cpp_type_name() const;
virtual std::string pb_type_name() const;
protected:
std::string get_type_name() const;
bool is_ptr() const;
std::string name() const;
bool is_optional() const;
std::string qualified_name() const;
protected:
const google::protobuf::FieldDescriptor *d_;
friend class OneofGenerator;
};
class OneofGenerator : public GeneratorBase {
public:
OneofGenerator(const google::protobuf::OneofDescriptor *d,
const Options &options)
: GeneratorBase(options), d_(d) {}
void generate_definition(google::protobuf::io::Printer *p);
void generate_calculate_size(google::protobuf::io::Printer *p);
void generate_serialization(google::protobuf::io::Printer *p,
const google::protobuf::FieldDescriptor *f);
void generate_deserialization(google::protobuf::io::Printer *p,
const google::protobuf::FieldDescriptor *f);
private:
int get_index(const google::protobuf::FieldDescriptor *f) const;
std::string name() const;
private:
const google::protobuf::OneofDescriptor *d_;
};
class MapFieldGenerator;
class FieldGeneratorMap {
public:
FieldGeneratorMap(const Descriptor *descriptor, const Options &options);
FieldGeneratorMap(const FieldGeneratorMap &) = delete;
FieldGeneratorMap &operator=(const FieldGeneratorMap &) = delete;
const FieldGenerator &get(const FieldDescriptor *field) const;
private:
const Descriptor *descriptor_;
std::vector<std::unique_ptr<FieldGenerator>> field_generators_;
static FieldGenerator *MakeGenerator(const FieldDescriptor *field,
const Options &options);
friend class MapFieldGenerator;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,199 +0,0 @@
#include "FileGenerator.h"
#include <google/protobuf/compiler/code_generator.h>
#include "EnumGenerator.h"
#include "MessageGenerator.h"
#include "helpers.hpp"
using namespace google::protobuf;
using namespace google::protobuf::compiler;
namespace struct_pb {
namespace compiler {
FileGenerator::FileGenerator(const google::protobuf::FileDescriptor *file,
Options options)
: GeneratorBase(options), file_(file) {
// std::vector<const Descriptor*> msgs = flatten_messages_in_file(file);
for (int i = 0; i < file_->message_type_count(); ++i) {
message_generators_.push_back(
std::make_unique<MessageGenerator>(file_->message_type(i), options));
}
for (int i = 0; i < file->enum_type_count(); ++i) {
enum_generators_.push_back(
std::make_unique<EnumGenerator>(file->enum_type(i), options));
}
}
void FileGenerator::generate_enum_definitions(
google::protobuf::io::Printer *p) {
for (int i = 0; i < enum_generators_.size(); ++i) {
enum_generators_[i]->generate_definition(p);
}
}
void FileGenerator::generate_message_definitions(
google::protobuf::io::Printer *p) {
for (int i = 0; i < message_generators_.size(); ++i) {
message_generators_[i]->generate_struct_definition(p);
}
}
void FileGenerator::generate_shared_header_code(
google::protobuf::io::Printer *p) {
generate_fwd_decls(p);
generate_enum_definitions(p);
generate_message_definitions(p);
}
void FileGenerator::generate_header(google::protobuf::io::Printer *p) {
p->Print(
{{"filename", file_->name()}},
R"(// Generated by the protocol buffer compiler (struct_pb). DO NOT EDIT!
// source: $filename$
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
#include "ylt/struct_pb.hpp"
)");
generate_dependency_includes(p);
generate_ns_open(p);
generate_shared_header_code(p);
generate_ns_close(p);
generate_message_struct_pb_func_definitions(p);
p->Print("// clang-format on\n");
}
void FileGenerator::generate_fwd_decls(google::protobuf::io::Printer *p) {
Formatter format(p);
for (int i = 0; i < file_->message_type_count(); ++i) {
auto m = file_->message_type(i);
format("struct $1$;\n", resolve_keyword(m->name()));
}
}
void FileGenerator::generate_dependency_includes(
google::protobuf::io::Printer *p) {
Formatter format(p);
for (int i = 0; i < file_->dependency_count(); ++i) {
auto dep = file_->dependency(i);
std::string basename = strip_proto(dep->name());
std::string header_name = basename + ".struct_pb.h";
format("#include \"$1$\"\n", header_name);
}
}
void FileGenerator::generate_source(google::protobuf::io::Printer *p) {
generate_message_struct_pb_func_source(p);
}
void FileGenerator::generate_message_struct_pb_func_definitions(
google::protobuf::io::Printer *p) {
std::vector<const Descriptor *> msgs = flatten_messages_in_file(file_);
Formatter format(p);
format("namespace struct_pb {\n");
format("namespace internal {\n");
for (auto msg : msgs) {
auto name = qualified_class_name(msg, options_);
format("// $1$\n", name);
format(
"template<>\n"
"std::size_t get_needed_size<$1$>(const $1$& t, const "
"::struct_pb::UnknownFields& unknown_fields);\n",
name);
format(
"template<>\n"
"void serialize_to<$1$>(char* data, std::size_t size, const $1$& t, "
"const "
"::struct_pb::UnknownFields& unknown_fields);\n",
name);
format(
"template<>\n"
"bool deserialize_to<$1$>($1$& t, const char* data, std::size_t size, "
"::struct_pb::UnknownFields& unknown_fields);\n",
name);
format(
"template<>\n"
"bool deserialize_to<$1$>($1$& t, const char* data, std::size_t "
"size);\n",
name);
format("\n");
}
format("} // internal\n");
format("} // struct_pb\n");
}
void FileGenerator::generate_message_struct_pb_func_source(
google::protobuf::io::Printer *p) {
std::vector<const Descriptor *> msgs = flatten_messages_in_file(file_);
Formatter format(p);
format("namespace struct_pb {\n");
format("namespace internal {\n");
for (auto msg : msgs) {
auto name = qualified_class_name(msg, options_);
MessageGenerator g(msg, options_);
format("// $1$\n", name);
format(
"template<>\n"
"std::size_t get_needed_size<$1$>(const $1$& t, const "
"::struct_pb::UnknownFields& unknown_fields) {\n",
name);
format.indent();
g.generate_get_needed_size(p);
format.outdent();
format(
"} // std::size_t get_needed_size<$1$>(const $1$& t, const "
"::struct_pb::UnknownFields& unknown_fields)\n",
name);
format(
"template<>\n"
"void serialize_to<$1$>(char* data, std::size_t size, const $1$& t, "
"const ::struct_pb::UnknownFields& unknown_fields) {\n",
name);
format.indent();
g.generate_serialize_to(p);
format.outdent();
format(
"} // void serialize_to<$1$>(char* data, std::size_t size, const $1$& "
"t, "
"const ::struct_pb::UnknownFields& unknown_fields)\n",
name);
format(
"template<>\n"
"bool deserialize_to<$1$>($1$& t, const char* data, std::size_t size, "
"::struct_pb::UnknownFields& unknown_fields) {\n",
name);
format.indent();
g.generate_deserialize_to(p);
format.outdent();
format("return true;\n");
format("} // bool deserialize_to<$1$>($1$&, const char*, std::size_t)\n",
name);
format("// end of $1$\n", name);
format(
"template<>\n"
"bool deserialize_to<$1$>($1$& t, const char* data, std::size_t size) "
"{\n",
name);
format.indent();
format("::struct_pb::UnknownFields unknown_fields{};\n");
format("return deserialize_to(t,data,size,unknown_fields);\n");
format.outdent();
format("}\n");
format("\n");
}
format("} // internal\n");
format("} // struct_pb\n");
}
void FileGenerator::generate_ns_open(google::protobuf::io::Printer *p) {
NamespaceOpener(p, options_.ns).open();
}
void FileGenerator::generate_ns_close(google::protobuf::io::Printer *p) {
NamespaceOpener(p, options_.ns).close();
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,35 +0,0 @@
#pragma once
#include "EnumGenerator.h"
#include "GeneratorBase.h"
#include "MessageGenerator.h"
#include "Options.hpp"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/printer.h"
namespace struct_pb {
namespace compiler {
class FileGenerator : public GeneratorBase {
public:
FileGenerator(const google::protobuf::FileDescriptor *file, Options options);
;
void generate_header(google::protobuf::io::Printer *p);
void generate_source(google::protobuf::io::Printer *p);
private:
void generate_shared_header_code(google::protobuf::io::Printer *p);
void generate_fwd_decls(google::protobuf::io::Printer *p);
void generate_enum_definitions(google::protobuf::io::Printer *p);
void generate_message_definitions(google::protobuf::io::Printer *p);
void generate_message_struct_pb_func_definitions(
google::protobuf::io::Printer *p);
void generate_message_struct_pb_func_source(google::protobuf::io::Printer *p);
void generate_dependency_includes(google::protobuf::io::Printer *p);
void generate_ns_open(google::protobuf::io::Printer *p);
void generate_ns_close(google::protobuf::io::Printer *p);
private:
const google::protobuf::FileDescriptor *file_;
std::vector<std::unique_ptr<MessageGenerator>> message_generators_;
std::vector<std::unique_ptr<EnumGenerator>> enum_generators_;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,26 +0,0 @@
#include "GeneratorBase.h"
#include <iostream>
namespace struct_pb {
namespace compiler {
// Convert lowerCamelCase and UpperCamelCase strings to lower_with_underscore.
std::string convert(const std::string &camelCase) {
std::string str(1, tolower(camelCase[0]));
// First place underscores between contiguous lower and upper case letters.
// For example, `_LowerCamelCase` becomes `_Lower_Camel_Case`.
for (auto it = camelCase.begin() + 1; it != camelCase.end(); ++it) {
if (isupper(*it) && *(it - 1) != '_' && islower(*(it - 1))) {
str += "_";
}
str += *it;
}
// Then convert it to lower case.
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,15 +0,0 @@
#pragma once
#include "Options.hpp"
#include "google/protobuf/descriptor.h"
namespace struct_pb {
namespace compiler {
using namespace google::protobuf;
class GeneratorBase {
public:
GeneratorBase(Options options) : options_(options) {}
protected:
Options options_;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,298 +0,0 @@
#include "MapFieldGenerator.h"
#include "EnumFieldGenerator.h"
#include "MessageFieldGenerator.h"
#include "PrimitiveFieldGenerator.h"
#include "StringFieldGenerator.h"
namespace struct_pb {
namespace compiler {
MapFieldGenerator::MapFieldGenerator(const FieldDescriptor *field,
const Options &options)
: FieldGenerator(field, options) {}
static std::string get_type_name_help(FieldDescriptor::Type type) {
switch (type) {
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return "double";
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
return "float";
case google::protobuf::FieldDescriptor::TYPE_INT64:
case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
case google::protobuf::FieldDescriptor::TYPE_SINT64:
return "int64_t";
case google::protobuf::FieldDescriptor::TYPE_UINT64:
case google::protobuf::FieldDescriptor::TYPE_FIXED64:
return "uint64_t";
case google::protobuf::FieldDescriptor::TYPE_UINT32:
case google::protobuf::FieldDescriptor::TYPE_FIXED32:
return "uint32_t";
case google::protobuf::FieldDescriptor::TYPE_INT32:
case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
case google::protobuf::FieldDescriptor::TYPE_SINT32:
return "int32_t";
case google::protobuf::FieldDescriptor::TYPE_BOOL:
return "bool";
case google::protobuf::FieldDescriptor::TYPE_STRING:
case google::protobuf::FieldDescriptor::TYPE_BYTES:
return "std::string";
case google::protobuf::FieldDescriptor::TYPE_GROUP:
// workaround for group
return "std::string";
default: {
return "not support now";
}
}
}
std::string MapFieldGenerator::cpp_type_name() const {
return "std::map<" + get_key_type_name() + ", " + get_value_type_name() + ">";
}
std::string MapFieldGenerator::get_value_type_name() const {
return get_kv_type_name_helper(d_->message_type()->field(1));
}
std::string MapFieldGenerator::get_key_type_name() const {
return get_kv_type_name_helper(d_->message_type()->field(0));
}
std::string MapFieldGenerator::get_kv_type_name_helper(
const FieldDescriptor *f) const {
if (f->type() == FieldDescriptor::TYPE_MESSAGE) {
return qualified_class_name(f->message_type(), options_);
}
else if (f->type() == FieldDescriptor::TYPE_ENUM) {
return qualified_enum_name(f->enum_type(), options_);
}
else {
return get_type_name_help(f->type());
}
}
void MapFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
generate_calculate_size_only(p, value);
}
void MapFieldGenerator::generate_calculate_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("for(const auto& e: $1$) {\n", value);
format.indent();
format("std::size_t map_entry_sz = 0;\n");
auto key_f = d_->message_type()->field(0);
auto value_f = d_->message_type()->field(1);
generate_calculate_kv_size(p, key_f, "e.first");
generate_calculate_kv_size(p, value_f, "e.second");
format("total += $1$ + calculate_varint_size(map_entry_sz) + map_entry_sz;",
calculate_tag_size(d_));
format.outdent();
format("}\n");
}
void MapFieldGenerator::generate_calculate_kv_size(
google::protobuf::io::Printer *p, const FieldDescriptor *f,
const std::string &value) const {
Formatter format(p);
switch (f->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE: {
p->Print({{"value", value}, {"tag_sz", calculate_tag_size(f)}}, R"(
std::size_t sz = get_needed_size($value$);
map_entry_sz += $tag_sz$ + calculate_varint_size(sz) + sz;
)");
break;
}
case FieldDescriptor::CPPTYPE_STRING: {
format(
"map_entry_sz += $1$ + calculate_varint_size($2$.size()) + "
"$2$.size();\n",
calculate_tag_size(f), value);
break;
}
case FieldDescriptor::CPPTYPE_ENUM: {
p->Print({{"tag_sz", calculate_tag_size(f)}},
R"(
map_entry_sz += $tag_sz$ + )");
EnumFieldGenerator(f, options_).generate_calculate_size_only(p, value);
format(";\n");
break;
}
default: {
format("map_entry_sz += $1$ + ", calculate_tag_size(f));
PrimitiveFieldGenerator(f, options_)
.generate_calculate_size_only(p, value);
format(";\n");
}
}
}
void MapFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
generate_serialization_only(p, value);
}
void MapFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
auto key_f = d_->message_type()->field(0);
auto value_f = d_->message_type()->field(1);
std::unique_ptr<FieldGenerator> kg;
kg.reset(FieldGeneratorMap::MakeGenerator(key_f, options_));
std::unique_ptr<FieldGenerator> vg;
kg.reset(FieldGeneratorMap::MakeGenerator(value_f, options_));
Formatter format(p);
format("for(const auto& e: $1$) {\n", value);
format.indent();
format("serialize_varint(data, pos, size, $1$);\n", calculate_tag_str(d_));
format("std::size_t map_entry_sz = 0;\n");
format("{\n");
format.indent();
generate_calculate_kv_size(p, key_f, "e.first");
format.outdent();
format("}\n");
format("{\n");
format.indent();
generate_calculate_kv_size(p, value_f, "e.second");
format.outdent();
format("}\n");
format("serialize_varint(data, pos, size, map_entry_sz);\n");
format("{\n");
format.indent();
generate_serialize_kv_only(p, key_f, "e.first");
format.outdent();
format("}\n");
format("{\n");
format.indent();
generate_serialize_kv_only(p, value_f, "e.second");
format.outdent();
format("}\n");
format.outdent();
format("}\n");
}
void MapFieldGenerator::generate_serialize_kv_only(
google::protobuf::io::Printer *p, const FieldDescriptor *f,
const std::string &value) const {
switch (f->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE: {
p->Print({{"value", value}, {"tag", calculate_tag_str(f)}}, R"(
serialize_varint(data, pos, size, $tag$);
std::size_t sz = get_needed_size($value$);
serialize_varint(data, pos, size, sz);
serialize_to(data + pos, sz, $value$);
pos += sz;
)");
break;
}
case FieldDescriptor::CPPTYPE_STRING: {
StringFieldGenerator(f, options_).generate_serialization_only(p, value);
break;
}
case FieldDescriptor::CPPTYPE_ENUM: {
EnumFieldGenerator(f, options_).generate_serialization_only(p, value);
break;
}
default: {
p->Print({{"tag", calculate_tag_str(f)}},
R"(
serialize_varint(data, pos, size, $tag$);
)");
PrimitiveFieldGenerator(f, options_)
.generate_serialization_only(p, value);
}
}
}
void MapFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print({{"tag", calculate_tag_str(d_)}},
R"(
case $tag$: {
uint64_t sz = 0;
ok = deserialize_varint(data, pos, size, sz);
if (!ok) {
return false;
}
std::size_t cur_max_size = pos + sz;
)");
generate_deserialization_only(p, value);
p->Print(R"(
break;
}
)");
}
void MapFieldGenerator::generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
auto key_f = d_->message_type()->field(0);
auto value_f = d_->message_type()->field(1);
Formatter format(p);
format("$1$ key_tmp_val{};\n", get_key_type_name());
format("$1$ value_tmp_val{};\n", get_value_type_name());
p->Print(
{
{"key_tag", calculate_tag_str(key_f)},
},
R"(
while (pos < cur_max_size) {
ok = read_tag(data, pos, size, tag);
if (!ok) {
return false;
}
switch(tag) {
case $key_tag$: {
)");
generate_deserialize_kv_only(p, key_f, "key_tmp_val", "cur_max_size");
p->Print(
{
{"value_tag", calculate_tag_str(value_f)},
},
R"(
break;
}
case $value_tag$: {
)");
generate_deserialize_kv_only(p, value_f, "value_tmp_val", "cur_max_size");
p->Print({{"value", value}}, R"(
break;
}
default: {
return false;
}
}
}
$value$[key_tmp_val] = std::move(value_tmp_val);
)");
}
void MapFieldGenerator::generate_deserialize_kv_only(
google::protobuf::io::Printer *p, const FieldDescriptor *f,
const std::string &value, const std::string &max_size) const {
switch (f->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE: {
p->Print({{"value", value},
{"tag", calculate_tag_str(f)},
{"max_size", max_size}},
R"(
uint64_t msg_sz = 0;
ok = deserialize_varint(data, pos, $max_size$, msg_sz);
if (!ok) {
return false;
}
ok = deserialize_to($value$, data + pos, msg_sz);
if (!ok) {
return false;
}
pos += msg_sz;
)");
break;
}
case FieldDescriptor::CPPTYPE_STRING: {
StringFieldGenerator(f, options_)
.generate_deserialization_only(p, value, "str_sz", max_size);
break;
}
case FieldDescriptor::CPPTYPE_ENUM: {
EnumFieldGenerator(f, options_)
.generate_deserialization_only(p, value, max_size);
break;
}
default: {
PrimitiveFieldGenerator(f, options_)
.generate_deserialization_only(p, value);
}
}
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,44 +0,0 @@
#pragma once
#include "FieldGenerator.h"
namespace struct_pb {
namespace compiler {
class MapFieldGenerator : public FieldGenerator {
public:
MapFieldGenerator(const FieldDescriptor *field, const Options &options);
std::string cpp_type_name() const override;
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_size_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
void generate_deserialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
private:
void generate_calculate_kv_size(google::protobuf::io::Printer *p,
const FieldDescriptor *f,
const std::string &value) const;
void generate_serialize_kv_only(google::protobuf::io::Printer *p,
const FieldDescriptor *f,
const std::string &value) const;
void generate_deserialize_kv_only(google::protobuf::io::Printer *p,
const FieldDescriptor *f,
const std::string &value,
const std::string &max_size) const;
private:
std::string get_key_type_name() const;
std::string get_value_type_name() const;
std::string get_kv_type_name_helper(const FieldDescriptor *f) const;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,149 +0,0 @@
#include "MessageFieldGenerator.h"
namespace struct_pb {
namespace compiler {
MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor *field,
const Options &options)
: FieldGenerator(field, options) {}
void MessageFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
if (can_ignore_default_value) {
p->Print({{"tag_sz", calculate_tag_size(d_)}, {"value", value}}, R"(
if ($value$) {
auto sz = get_needed_size(*$value$);
total += $tag_sz$ + calculate_varint_size(sz) + sz;
}
)");
}
else {
p->Print({{"tag_sz", calculate_tag_size(d_)}, {"value", value}}, R"(
auto sz = get_needed_size(*$value$);
total += $tag_sz$ + calculate_varint_size(sz) + sz;
)");
}
}
void MessageFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
p->Print({{"value", value}, {"tag", calculate_tag_str(d_)}}, R"(
if ($value$) {
serialize_varint(data, pos, size, $tag$);
auto sz = get_needed_size(*$value$);
serialize_varint(data, pos, size, sz);
serialize_to(data + pos, sz, *$value$);
pos += sz;
}
)");
}
void MessageFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print({{"value", value},
{"tag", calculate_tag_str(d_)},
{"classname", qualified_class_name(d_->message_type(), options_)}},
R"(case $tag$: {
if (!$value$) {
$value$ = std::make_unique<$classname$>();
}
uint64_t sz = 0;
ok = deserialize_varint(data, pos, size, sz);
if (!ok) {
return false;
}
ok = deserialize_to(*$value$, data + pos, sz);
if (!ok) {
return false;
}
pos += sz;
break;
}
)");
}
std::string MessageFieldGenerator::cpp_type_name() const {
return "std::unique_ptr<" +
qualified_class_name(d_->message_type(), options_) + ">";
}
RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator(
const FieldDescriptor *field, const Options &options)
: FieldGenerator(field, options) {}
void RepeatedMessageFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
if (can_ignore_default_value) {
format("if (!$1$.empty()) {\n", value);
format.indent();
generate_calculate_size_only(p, value);
format.outdent();
format("}\n");
}
else {
generate_calculate_size_only(p, value);
}
}
void RepeatedMessageFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
if (can_ignore_default_value) {
format("if (!$1$.empty()) {\n", value);
format.indent();
generate_serialization_only(p, value);
format.outdent();
format("}\n");
}
else {
generate_serialization_only(p, value);
}
}
void RepeatedMessageFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print({{"tag", std::to_string(calculate_tag(d_))},
{"value", value},
{"classname", qualified_class_name(d_->message_type(), options_)}},
R"(case $tag$: {
uint64_t sz = 0;
ok = deserialize_varint(data, pos, size, sz);
if (!ok) {
return false;
}
$value$.emplace_back();
ok = deserialize_to($value$.back(), data + pos, sz);
if (!ok) {
$value$.pop_back();
return false;
}
pos += sz;
break;
}
)");
}
std::string RepeatedMessageFieldGenerator::cpp_type_name() const {
return "std::vector<" + qualified_class_name(d_->message_type(), options_) +
">";
}
void RepeatedMessageFieldGenerator::generate_calculate_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print({{"tag_sz", calculate_tag_size(d_)}, {"value", value}}, R"(
for(const auto& e: $value$) {
std::size_t sz = get_needed_size(e);
total += $tag_sz$ + calculate_varint_size(sz) + sz;
}
)");
}
void RepeatedMessageFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
p->Print({{"tag", std::to_string(calculate_tag(d_))}, {"value", value}}, R"(
for(const auto& e: $value$) {
serialize_varint(data, pos, size, $tag$);
std::size_t sz = get_needed_size(e);
serialize_varint(data, pos, size, sz);
serialize_to(data+pos, sz, e);
pos += sz;
}
)");
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,38 +0,0 @@
#pragma once
#include "FieldGenerator.h"
namespace struct_pb {
namespace compiler {
class MessageFieldGenerator : public FieldGenerator {
public:
MessageFieldGenerator(const FieldDescriptor *field, const Options &options);
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
std::string cpp_type_name() const override;
};
class RepeatedMessageFieldGenerator : public FieldGenerator {
public:
RepeatedMessageFieldGenerator(const FieldDescriptor *field,
const Options &options);
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_size_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
std::string cpp_type_name() const override;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,209 +0,0 @@
#include "MessageGenerator.h"
#include "EnumGenerator.h"
#include "FieldGenerator.h"
#include "helpers.hpp"
using namespace google::protobuf;
using namespace google::protobuf::internal;
namespace struct_pb {
namespace compiler {
std::unordered_map<std::string_view, std::string> ClassVars(
const Descriptor *descriptor, Options options) {
std::unordered_map<std::string_view, std::string> vars{};
vars.emplace("classname", resolve_keyword(descriptor->name()));
vars.emplace("classtype", qualified_class_name(descriptor, options));
return vars;
}
// copy from cpp compiler
static std::string UnderscoresToCamelCase(const std::string &input,
bool cap_next_letter) {
std::string result;
// Note: I distrust ctype.h due to locales.
for (int i = 0; i < input.size(); i++) {
if ('a' <= input[i] && input[i] <= 'z') {
if (cap_next_letter) {
result += input[i] + ('A' - 'a');
}
else {
result += input[i];
}
cap_next_letter = false;
}
else if ('A' <= input[i] && input[i] <= 'Z') {
// Capital letters are left as-is.
result += input[i];
cap_next_letter = false;
}
else if ('0' <= input[i] && input[i] <= '9') {
result += input[i];
cap_next_letter = true;
}
else {
cap_next_letter = true;
}
}
return result;
}
void MessageGenerator::generate_struct_definition(
google::protobuf::io::Printer *p) {
// auto v = p->WithVars(ClassVars(d_, options_));
Formatter format(p);
format("struct $1$ {\n", resolve_keyword(d_->name()));
format.indent();
for (int i = 0; i < d_->enum_type_count(); ++i) {
auto e = d_->enum_type(i);
EnumGenerator(e, options_).generate_definition(p);
}
for (int i = 0; i < d_->oneof_decl_count(); ++i) {
auto oneof = d_->oneof_decl(i);
std::string enum_name = UnderscoresToCamelCase(oneof->name(), true);
format("enum class $1$Case {\n", enum_name);
format.indent();
format("none = 0,\n");
for (int j = 0; j < oneof->field_count(); ++j) {
auto f = oneof->field(j);
format("$1$ = $2$,\n", resolve_keyword(f->name()), f->number());
}
format.outdent();
format("};\n");
format("$1$Case $2$_case() const {\n", enum_name, oneof->name());
format.indent();
format("switch ($1$.index()) {\n", resolve_keyword(oneof->name()));
format.indent();
for (int j = 0; j < oneof->field_count(); ++j) {
format("case $1$:\n", j + 1);
format.indent();
format("return $1$Case::$2$;\n", enum_name,
resolve_keyword(oneof->field(j)->name()));
format.outdent();
}
format("default:\n");
format.indent();
format("return $1$Case::none;\n", enum_name);
format.outdent();
format.outdent();
format("}\n");
format.outdent();
format("}\n");
format("\n");
}
for (int i = 0; i < d_->nested_type_count(); ++i) {
auto t = d_->nested_type(i);
auto name = t->name();
if (is_map_entry(t)) {
continue;
}
MessageGenerator(t, options_).generate_struct_definition(p);
}
std::set<const OneofDescriptor *> oneof_set;
for (int i = 0; i < d_->field_count(); ++i) {
auto f = d_->field(i);
auto oneof = f->containing_oneof();
if (oneof) {
if (oneof_set.find(oneof) == oneof_set.end()) {
OneofGenerator(oneof, options_).generate_definition(p);
oneof_set.insert(oneof);
}
else {
continue;
}
}
else {
fg_map_.get(f).generate_definition(p);
}
}
if (options_.generate_eq_op) {
format("bool operator==(const $1$&) const = default;\n", class_name(d_));
}
// format("std::size_t get_needed_size() const;\n");
// format("std::string SerializeAsString() const;\n");
// format("bool ParseFromArray(const char* data, std::size_t size);\n");
format.outdent();
format("};\n");
}
void MessageGenerator::generate_source(google::protobuf::io::Printer *p) {
generate_get_needed_size(p);
Formatter format(p);
format("std::string $1$::SerializeAsString() const {\n", d_->name());
format.indent();
format("std::string buffer;\n");
format("// calculate buffer size\n");
format("std::size_t total = get_needed_size();\n");
format("std::size_t pos = 0;\n");
format("buffer.resize(total);\n");
for (int i = 0; i < d_->field_count(); ++i) {
auto f = d_->field(i);
// FieldGenerator(f, options_).generate_calculate_size(p);
}
format("return buffer;\n");
format.outdent();
format("}\n");
}
void MessageGenerator::generate_get_needed_size(
google::protobuf::io::Printer *p) {
Formatter format(p);
format("std::size_t total = unknown_fields.total_size();\n");
std::set<const OneofDescriptor *> oneof_set;
for (int i = 0; i < d_->field_count(); ++i) {
auto f = d_->field(i);
fg_map_.get(f).generate_calculate_size(p, true);
}
format("return total;\n");
}
void MessageGenerator::generate_serialize_to(google::protobuf::io::Printer *p) {
Formatter format(p);
format("std::size_t pos = 0;\n");
std::vector<const FieldDescriptor *> fs;
fs.reserve(d_->field_count());
for (int i = 0; i < d_->field_count(); ++i) {
fs.push_back(d_->field(i));
}
std::sort(fs.begin(), fs.end(),
[](const FieldDescriptor *lhs, const FieldDescriptor *rhs) {
return lhs->number() < rhs->number();
});
for (int i = 0; i < d_->field_count(); ++i) {
auto f = fs[i];
format("{\n");
format.indent();
fg_map_.get(f).generate_serialization(p, true);
format.outdent();
format("}\n");
}
format("unknown_fields.serialize_to(data, pos, size);\n");
}
void MessageGenerator::generate_deserialize_to(
google::protobuf::io::Printer *p) {
Formatter format(p);
format("std::size_t pos = 0;\n");
format("bool ok = false;\n");
format("while (pos < size) {\n");
format.indent();
p->Print(R"(uint64_t tag = 0;
ok = read_tag(data, pos, size, tag);
if (!ok) {
return false;
}
switch(tag) {
)");
format.indent();
for (int i = 0; i < d_->field_count(); ++i) {
auto f = d_->field(i);
fg_map_.get(f).generate_deserialization(p);
}
format("default: {\n");
format.indent();
format("ok = deserialize_unknown(data, pos, size, tag, unknown_fields);\n");
format("return ok;\n");
format.outdent();
format("}\n");
format.outdent();
format("}\n");
format.outdent();
format("}\n");
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,33 +0,0 @@
#pragma once
#include "FieldGenerator.h"
#include "GeneratorBase.h"
namespace struct_pb {
namespace compiler {
class FileGenerator;
class MessageGenerator : public GeneratorBase {
public:
MessageGenerator(const google::protobuf::Descriptor *d, Options options)
: GeneratorBase(options), d_(d), fg_map_(d, options) {}
void generate(google::protobuf::io::Printer *p);
void generate_struct_definition(google::protobuf::io::Printer *p);
void generate_source(google::protobuf::io::Printer *p);
private:
void generate_get_needed_size(google::protobuf::io::Printer *p);
void generate_serialize_to(google::protobuf::io::Printer *p);
void generate_deserialize_to(google::protobuf::io::Printer *p);
private:
const google::protobuf::Descriptor *d_;
std::vector<std::string> field_name_list;
std::vector<std::size_t> unpack_index_list;
std::vector<std::size_t> field_number_list;
std::map<std::size_t, std::size_t> n2i_map;
std::map<std::size_t, std::size_t> i2n_map;
FieldGeneratorMap fg_map_;
friend class FileGenerator;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,323 +0,0 @@
#include "OneofFieldGenerator.h"
#include "EnumFieldGenerator.h"
#include "PrimitiveFieldGenerator.h"
#include "StringFieldGenerator.h"
#include "helpers.hpp"
namespace struct_pb {
namespace compiler {
void OneofFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
format("if ($1$.index() == $2$) {\n", value, index());
generate_calculate_size_only(p, value);
format("\n}\n");
}
std::string OneofFieldGenerator::index() const {
auto oneof = d_->containing_oneof();
assert(oneof);
int index = 0;
for (int i = 0; i < oneof->field_count(); ++i) {
if (oneof->field(i) == d_) {
index = i + 1;
break;
}
}
assert(index > 0);
return std::to_string(index);
}
void OneofFieldGenerator::generate_calculate_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
auto oneof_value = "std::get<" + index() + ">(" + value + ")";
switch (d_->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE: {
p->Print({{"tag_sz", calculate_tag_size(d_)},
{"index", index()},
{"oneof_value", oneof_value},
{"value", value}},
R"(
uint64_t sz = 0;
if ($oneof_value$) {
sz = get_needed_size(*$oneof_value$);
}
total += $tag_sz$ + calculate_varint_size(sz) + sz;
)");
break;
}
case FieldDescriptor::CPPTYPE_STRING: {
p->Print(
{
{"tag_sz", calculate_tag_size(d_)},
},
R"(
total += $tag_sz$ + calculate_varint_size()");
StringFieldGenerator(d_, options_)
.generate_calculate_size_only(p, oneof_value);
p->Print(") + ");
StringFieldGenerator(d_, options_)
.generate_calculate_size_only(p, oneof_value);
p->Print(";\n");
break;
}
case FieldDescriptor::CPPTYPE_ENUM: {
format("total += $1$ + ", calculate_tag_size(d_));
EnumFieldGenerator(d_, options_)
.generate_calculate_size_only(p, oneof_value);
format(";\n");
break;
}
default: {
format("total += $1$ + ", calculate_tag_size(d_));
PrimitiveFieldGenerator(d_, options_)
.generate_calculate_size_only(p, oneof_value);
format(";\n");
}
}
}
void OneofFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
format("if ($1$.index() == $2$) {\n", value, index());
generate_serialization_only(p, value);
format("\n}\n");
}
void OneofFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
auto oneof_value = "std::get<" + index() + ">(" + value + ")";
switch (d_->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE: {
p->Print({{"value", value},
{"tag", std::to_string(calculate_tag(d_))},
{"index", index()},
{"oneof_value", oneof_value}
},
R"(
serialize_varint(data, pos, size, $tag$);
if ($oneof_value$) {
std::size_t sz = get_needed_size(*$oneof_value$);
serialize_varint(data, pos, size, sz);
serialize_to(data + pos, sz, *$oneof_value$);
pos += sz;
}
else {
serialize_varint(data, pos, size, 0);
}
)");
break;
}
case FieldDescriptor::CPPTYPE_STRING: {
StringFieldGenerator(d_, options_)
.generate_serialization_only(p, oneof_value);
break;
}
case FieldDescriptor::CPPTYPE_ENUM: {
EnumFieldGenerator(d_, options_)
.generate_serialization_only(p, oneof_value);
break;
}
default: {
p->Print({{"tag", calculate_tag_str(d_)}},
R"(
serialize_varint(data, pos, size, $tag$);
)");
PrimitiveFieldGenerator(d_, options_)
.generate_serialization_only(p, oneof_value);
}
}
}
void OneofFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("case $1$: {\n", calculate_tag_str(d_));
generate_deserialization_only(p, value);
format("break;\n");
format("}\n");
}
void OneofFieldGenerator::generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &value,
const std::string &max_size) const {
Formatter format(p);
std::string oneof_value = "std::get<" + index() + ">(" + value + ")";
switch (d_->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE: {
p->Print(
{{"value", value},
{"type_name", qualified_class_name(d_->message_type(), options_)},
{"tag", std::to_string(calculate_tag(d_))},
{"max_size", max_size},
{"oneof_value", oneof_value},
{"index", index()}},
R"(
if ($value$.index() != $index$) {
$value$.emplace<$index$>(new $type_name$());
}
uint64_t msg_sz = 0;
ok = deserialize_varint(data, pos, $max_size$, msg_sz);
if (!ok) {
return false;
}
ok = deserialize_to(*$oneof_value$, data + pos, msg_sz);
if (!ok) {
return false;
}
pos += msg_sz;
)");
break;
}
case FieldDescriptor::CPPTYPE_STRING: {
format("if ($1$.index() != $2$) {\n", value, index());
format.indent();
format("$1$.emplace<$2$>();\n", value, index());
format.outdent();
format("}\n");
StringFieldGenerator(d_, options_)
.generate_deserialization_only(p, oneof_value, "str_sz", max_size);
break;
}
case FieldDescriptor::CPPTYPE_ENUM: {
format("if ($1$.index() != $2$) {\n", value, index());
format.indent();
format("$1$.emplace<$2$>();\n", value, index());
format.outdent();
format("}\n");
EnumFieldGenerator(d_, options_)
.generate_deserialization_only(p, oneof_value, max_size);
break;
}
default: {
format("if ($1$.index() != $2$) {\n", value, index());
format.indent();
format("$1$.emplace<$2$>();\n", value, index());
format.outdent();
format("}\n");
PrimitiveFieldGenerator(d_, options_)
.generate_deserialization_only(p, oneof_value);
}
}
}
std::string OneofFieldGenerator::cpp_type_name() const {
return "std::variant";
}
std::string OneofFieldGenerator::pb_type_name() const {
return d_->type_name();
}
OneofFieldGenerator::OneofFieldGenerator(const FieldDescriptor *descriptor,
const Options &options)
: FieldGenerator(descriptor, options) {}
MessageOneofFieldGenerator::MessageOneofFieldGenerator(
const FieldDescriptor *descriptor, const Options &options)
: OneofFieldGenerator(descriptor, options) {}
void MessageOneofFieldGenerator::generate_setter_and_getter(
google::protobuf::io::Printer *p) {
auto oneof = d_->containing_oneof();
assert(oneof);
std::string type_name = get_type_name();
type_name = qualified_class_name(d_->message_type(), options_);
p->Print({{"name", resolve_keyword(d_->name())},
{"type_name", type_name},
{"field_name", resolve_keyword(oneof->name())},
{"index", index()}},
R"(
bool has_$name$() const {
return $field_name$.index() == $index$;
}
void set_allocated_$name$($type_name$* p) {
assert(p);
$field_name$.emplace<$index$>(p);
}
const std::unique_ptr<$type_name$>& $name$() const {
assert($field_name$.index() == $index$);
return std::get<$index$>($field_name$);
}
)");
}
PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator(
const FieldDescriptor *descriptor, const Options &options)
: OneofFieldGenerator(descriptor, options) {}
void PrimitiveOneofFieldGenerator::generate_setter_and_getter(
google::protobuf::io::Printer *p) {
auto oneof = d_->containing_oneof();
assert(oneof);
std::string type_name = get_type_name();
p->Print({{"name", resolve_keyword(d_->name())},
{"type_name", type_name},
{"field_name", resolve_keyword(oneof->name())},
{"index", index()}},
R"(
bool has_$name$() const {
return $field_name$.index() == $index$;
}
void set_$name$($type_name$ $name$) {
$field_name$.emplace<$index$>($name$);
}
$type_name$ $name$() const {
assert($field_name$.index() == $index$);
return std::get<$index$>($field_name$);
}
)");
}
StringOneofFieldGenerator::StringOneofFieldGenerator(
const FieldDescriptor *descriptor, const Options &options)
: OneofFieldGenerator(descriptor, options) {}
void StringOneofFieldGenerator::generate_setter_and_getter(
google::protobuf::io::Printer *p) {
auto oneof = d_->containing_oneof();
assert(oneof);
std::string type_name = get_type_name();
p->Print({{"name", resolve_keyword(d_->name())},
{"type_name", type_name},
{"field_name", resolve_keyword(oneof->name())},
{"index", index()}},
R"(
bool has_$name$() const {
return $field_name$.index() == $index$;
}
void set_$name$($type_name$ $name$) {
$field_name$.emplace<$index$>(std::move($name$));
}
const $type_name$& $name$() const {
assert($field_name$.index() == $index$);
return std::get<$index$>($field_name$);
}
)");
}
EnumOneofFieldGenerator::EnumOneofFieldGenerator(
const FieldDescriptor *descriptor, const Options &options)
: OneofFieldGenerator(descriptor, options) {}
void EnumOneofFieldGenerator::generate_setter_and_getter(
google::protobuf::io::Printer *p) {
auto oneof = d_->containing_oneof();
assert(oneof);
std::string type_name = get_type_name();
type_name = qualified_enum_name(d_->enum_type(), options_);
p->Print({{"name", resolve_keyword(d_->name())},
{"type_name", type_name},
{"field_name", resolve_keyword(oneof->name())},
{"index", index()}},
R"(
bool has_$name$() const {
return $field_name$.index() == $index$;
}
void set_allocated_$name$($type_name$ p) {
$field_name$.emplace<$index$>(p);
}
$type_name$ $name$() const {
assert($field_name$.index() == $index$);
return std::get<$index$>($field_name$);
}
)");
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,64 +0,0 @@
#pragma once
#include "FieldGenerator.h"
namespace struct_pb {
namespace compiler {
class OneofFieldGenerator : public FieldGenerator {
public:
OneofFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_size_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
void generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &value,
const std::string &max_size = "size") const;
std::string cpp_type_name() const override;
std::string pb_type_name() const override;
virtual void generate_setter_and_getter(google::protobuf::io::Printer *p){};
protected:
std::string index() const;
};
class MessageOneofFieldGenerator : public OneofFieldGenerator {
public:
MessageOneofFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
void generate_setter_and_getter(google::protobuf::io::Printer *p) override;
};
class StringOneofFieldGenerator : public OneofFieldGenerator {
public:
StringOneofFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
void generate_setter_and_getter(google::protobuf::io::Printer *p) override;
};
class EnumOneofFieldGenerator : public OneofFieldGenerator {
public:
EnumOneofFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
void generate_setter_and_getter(google::protobuf::io::Printer *p) override;
};
class PrimitiveOneofFieldGenerator : public OneofFieldGenerator {
public:
PrimitiveOneofFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
void generate_setter_and_getter(google::protobuf::io::Printer *p) override;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,10 +0,0 @@
#pragma once
#include <string>
#include "google/protobuf/descriptor.h"
struct Options {
Options(const google::protobuf::FileDescriptor* f) : f(f) {}
bool generate_eq_op = false;
std::string ns;
const google::protobuf::FileDescriptor* f;
};

View File

@ -1,453 +0,0 @@
#include "PrimitiveFieldGenerator.h"
namespace struct_pb {
namespace compiler {
std::string get_type_name_help(FieldDescriptor::Type type) {
switch (type) {
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
return "double";
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
return "float";
case google::protobuf::FieldDescriptor::TYPE_INT64:
case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
case google::protobuf::FieldDescriptor::TYPE_SINT64:
return "int64_t";
case google::protobuf::FieldDescriptor::TYPE_UINT64:
case google::protobuf::FieldDescriptor::TYPE_FIXED64:
return "uint64_t";
case google::protobuf::FieldDescriptor::TYPE_UINT32:
case google::protobuf::FieldDescriptor::TYPE_FIXED32:
return "uint32_t";
case google::protobuf::FieldDescriptor::TYPE_INT32:
case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
case google::protobuf::FieldDescriptor::TYPE_SINT32:
return "int32_t";
case google::protobuf::FieldDescriptor::TYPE_BOOL:
return "bool";
default: {
// can't reach, dead path
return "not support";
}
}
}
PrimitiveFieldGenerator::PrimitiveFieldGenerator(
const FieldDescriptor *descriptor, const Options &options)
: FieldGenerator(descriptor, options) {}
void PrimitiveFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
if (is_optional()) {
format("if ($1$.has_value()) {\n", value);
format.indent();
format("total += $1$ + ", calculate_tag_size(d_));
generate_calculate_size_only(p, value + ".value()");
format(";\n");
format.outdent();
format("}\n");
}
else {
if (can_ignore_default_value) {
format("if ($1$ != 0) {\n", value);
format.indent();
format("total += $1$ + ", calculate_tag_size(d_));
generate_calculate_size_only(p, value);
format(";\n");
format.outdent();
format("}\n");
}
else {
format("total += $1$ + ", calculate_tag_size(d_));
generate_calculate_size_only(p, value);
format(";\n");
}
}
}
void PrimitiveFieldGenerator::generate_calculate_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
if (is_varint(d_)) {
if (is_sint(d_)) {
format("calculate_varint_size(encode_zigzag($1$))", value);
}
else if (is_bool(d_)) {
format("calculate_varint_size(static_cast<uint64_t>($1$))", value);
}
else {
format("calculate_varint_size($1$)", value);
}
}
else if (is_i64(d_)) {
format("8");
}
else if (is_i32(d_)) {
format("4");
}
}
std::string PrimitiveFieldGenerator::cpp_type_name() const {
std::string type_name = get_type_name_help(d_->type());
if (is_optional()) {
type_name = "std::optional<" + type_name + ">";
}
return type_name;
}
void PrimitiveFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
if (is_optional()) {
format("if ($1$.has_value()) {\n", value);
format.indent();
format("serialize_varint(data, pos, size, $1$);\n", calculate_tag_str(d_));
generate_serialization_only(p, value + ".value()");
format("\n");
format.outdent();
format("}\n");
}
else {
if (can_ignore_default_value) {
format("if ($1$ != 0) {\n", value);
format.indent();
format("serialize_varint(data, pos, size, $1$);\n",
calculate_tag_str(d_));
generate_serialization_only(p, value);
format("\n");
format.outdent();
format("}\n");
}
else {
format("serialize_varint(data, pos, size, $1$);\n",
calculate_tag_str(d_));
generate_serialization_only(p, value);
}
}
}
void PrimitiveFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
if (is_varint(d_)) {
if (is_sint(d_)) {
format("serialize_varint(data, pos, size, encode_zigzag($1$));", value);
}
else if (is_bool(d_)) {
format("serialize_varint(data, pos, size, static_cast<uint64_t>($1$));",
value);
}
else {
if (d_->cpp_type() != FieldDescriptor::CPPTYPE_UINT64) {
format("serialize_varint(data, pos, size, static_cast<uint64_t>($1$));",
value);
}
else {
format("serialize_varint(data, pos, size, $1$);", value);
}
}
}
else if (is_i64(d_) || is_i32(d_)) {
std::size_t sz = is_i64(d_) ? 8 : 4;
p->Print({{"value", value}, {"sz", std::to_string(sz)}}, R"(
std::memcpy(data + pos, &$value$, $sz$);
pos += $sz$;
)");
}
}
void PrimitiveFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("case $1$: {\n", calculate_tag_str(d_));
format.indent();
generate_deserialization_only(p, value);
format("break;\n");
format.outdent();
format("}\n");
}
void PrimitiveFieldGenerator::generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &output,
const std::string &max_size) const {
if (is_varint(d_)) {
p->Print({{"output", output},
{"type_name", get_type_name_help(d_->type())},
{"max_size", max_size}},
R"(
uint64_t varint_tmp = 0;
ok = deserialize_varint(data, pos, $max_size$, varint_tmp);
if (!ok) {
return false;
}
)");
if (is_sint(d_)) {
if (is_sint32(d_)) {
p->Print(
{
{"output", output},
{"type_name", get_type_name_help(d_->type())},
{"max_size", max_size}},
R"(
$output$ = static_cast<$type_name$>(decode_zigzag(uint32_t(varint_tmp)));
)");
}
else {
p->Print(
{
{"output", output},
{"type_name", get_type_name_help(d_->type())},
{"max_size", max_size}},
R"(
$output$ = static_cast<$type_name$>(decode_zigzag(varint_tmp));
)");
}
}
else if (is_bool(d_)) {
p->Print(
{
{"output", output},
{"type_name", get_type_name_help(d_->type())},
{"max_size", max_size}},
R"(
$output$ = static_cast<$type_name$>(varint_tmp);
)");
}
else {
p->Print(
{
{"output", output},
{"type_name", get_type_name_help(d_->type())},
{"max_size", max_size}},
R"(
$output$ = varint_tmp;
)");
}
}
else if (is_i64(d_) || is_i32(d_)) {
auto sz = is_i64(d_) ? 8 : 4;
p->Print(
{
{"output", output},
{"type_name", get_type_name_help(d_->type())},
{"max_size", max_size},
{"sz", std::to_string(sz)}},
R"(
if (pos + $sz$ > $max_size$) {
return false;
}
$type_name$ fixed_tmp = 0;
std::memcpy(&fixed_tmp, data + pos, $sz$);
pos += $sz$;
$output$ = fixed_tmp;
)");
}
}
RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
const FieldDescriptor *descriptor, const Options &options)
: FieldGenerator(descriptor, options) {}
void RepeatedPrimitiveFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
PrimitiveFieldGenerator g(d_, options_);
if (is_packed()) {
format("if (!$1$.empty()) {\n", value);
format.indent();
generate_calculate_packed_size_only(p, value);
format(
"total += $1$ + calculate_varint_size(container_total) + "
"container_total;\n",
calculate_tag_size(d_));
format.outdent();
format("}\n");
}
else {
format("if (!$1$.empty()) {\n", value);
format.indent();
generate_calculate_unpacked_size_only(p, value);
format("total += container_total;\n");
format.outdent();
format("}\n");
}
}
bool RepeatedPrimitiveFieldGenerator::is_packed() const {
return d_->is_packable() && d_->is_packed();
}
std::string RepeatedPrimitiveFieldGenerator::cpp_type_name() const {
return "std::vector<" + get_type_name_help(d_->type()) + ">";
}
void RepeatedPrimitiveFieldGenerator::generate_calculate_packed_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
PrimitiveFieldGenerator g(d_, options_);
if (is_varint(d_)) {
p->Print({{"value", value}},
R"(
std::size_t container_total = 0;
for(const auto& e: $value$) {
container_total += )");
g.generate_calculate_size_only(p, "e");
format(";\n");
format("}\n");
}
else if (is_i64(d_) || is_i32(d_)) {
auto sz = is_i64(d_) ? 8 : 4;
p->Print({{"sz", std::to_string(sz)}, {"value", value}}, R"(
std::size_t container_total = $sz$ * $value$.size();
)");
}
}
void RepeatedPrimitiveFieldGenerator::generate_calculate_unpacked_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
PrimitiveFieldGenerator g(d_, options_);
Formatter format(p);
if (is_varint(d_)) {
p->Print({{"value", value}, {"tag_sz", calculate_tag_size(d_, true)}},
R"(
std::size_t container_total = 0;
for(const auto& e: $value$) {
container_total += $tag_sz$;
container_total += )");
g.generate_calculate_size_only(p, "e");
format(";\n");
format("}\n");
}
else if (is_i64(d_) || is_i32(d_)) {
auto sz = is_i64(d_) ? 8 : 4;
p->Print({{"sz", std::to_string(sz)},
{"value", value},
{"tag_sz", calculate_tag_size(d_, true)}},
R"(
std::size_t container_total = ($tag_sz$ + $sz$) * $value$.size();
)");
}
}
void RepeatedPrimitiveFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
PrimitiveFieldGenerator g(d_, options_);
Formatter format(p);
if (is_packed()) {
if (is_varint(d_)) {
format("if (!$1$.empty()) {\n", value);
format.indent();
format("serialize_varint(data, pos, size, $1$);\n",
calculate_tag_str(d_));
generate_calculate_packed_size_only(p, value);
format("serialize_varint(data, pos, size, container_total);\n");
format("for(const auto& v: $1$) {\n", value);
format.indent();
g.generate_serialization_only(p, "v");
format.outdent();
format("}\n");
format.outdent();
format("}\n");
}
else if (is_i64(d_) || is_i32(d_)) {
format("if (!$1$.empty()) {\n", value);
format.indent();
format("serialize_varint(data, pos, size, $1$);\n",
calculate_tag_str(d_));
generate_calculate_packed_size_only(p, value);
format("serialize_varint(data, pos, size, container_total);\n");
format("std::memcpy(data + pos, $1$.data(), container_total);\n", value);
format.outdent();
format("}\n");
}
}
else {
format("for(const auto& v: $1$) {\n", value);
format.indent();
format("serialize_varint(data, pos, size, $1$);\n",
calculate_tag_str(d_, true));
g.generate_serialization_only(p, "v");
format.outdent();
format("}\n");
}
}
void RepeatedPrimitiveFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("case $1$: {\n", unpacked_tag());
format.indent();
format("$1$ e{};\n", get_type_name_help(d_->type()));
generate_deserialization_unpacked_only(p, "e");
format("$1$.push_back(e);\n", value);
format("break;\n");
format.outdent();
format("}\n");
format("case $1$: {\n", packed_tag());
format.indent();
format("uint64_t sz = 0;\n");
format("ok = deserialize_varint(data, pos, size, sz);\n");
format("if (!ok) {\n");
format.indent();
format("return false;\n");
format.outdent();
format("}\n");
format("std::size_t cur_max_size = pos + sz;\n");
generate_deserialization_packed_only(p, value, "cur_max_size");
format("break;\n");
format.outdent();
format("}\n");
}
void RepeatedPrimitiveFieldGenerator::generate_deserialization_packed_only(
google::protobuf::io::Printer *p, const std::string &value,
const std::string &max_size) const {
if (is_varint(d_)) {
Formatter format(p);
format("while (pos < $1$) {\n", max_size);
format.indent();
format("$1$ e{};\n", get_type_name_help(d_->type()));
PrimitiveFieldGenerator g(d_, options_);
g.generate_deserialization_only(p, "e");
format("$1$.push_back(e);\n", value);
format.outdent();
format("}\n");
}
else if (is_i64(d_) || is_i32(d_)) {
auto sz = is_i64(d_) ? 8 : 4;
p->Print({{"value", value}, {"sz", std::to_string(sz)}}, R"(
int count = sz / $sz$;
if ($sz$ * count != sz) {
return false;
}
if (pos + sz > size) {
return false;
}
$value$.resize(count);
std::memcpy($value$.data(), data + pos, sz);
pos += sz;
)");
}
}
std::string RepeatedPrimitiveFieldGenerator::packed_tag() const {
uint32_t number = d_->number();
uint32_t wire_type = 2;
auto tag = number << 3 | wire_type;
return std::to_string(tag);
}
std::string RepeatedPrimitiveFieldGenerator::unpacked_tag() const {
uint32_t number = d_->number();
uint32_t wire_type = 0;
if (is_varint(d_)) {
wire_type = 0;
}
else if (is_i64(d_)) {
wire_type = 1;
}
else if (is_i32(d_)) {
wire_type = 5;
}
auto tag = number << 3 | wire_type;
return std::to_string(tag);
}
void RepeatedPrimitiveFieldGenerator::generate_deserialization_unpacked_only(
google::protobuf::io::Printer *p, const std::string &output) const {
PrimitiveFieldGenerator g(d_, options_);
g.generate_deserialization_only(p, output);
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,59 +0,0 @@
#pragma once
#include "FieldGenerator.h"
#include "google/protobuf/descriptor.h"
namespace struct_pb {
namespace compiler {
using namespace google::protobuf;
class PrimitiveFieldGenerator : public FieldGenerator {
public:
PrimitiveFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
PrimitiveFieldGenerator(const PrimitiveFieldGenerator &) = delete;
PrimitiveFieldGenerator &operator=(const PrimitiveFieldGenerator &) = delete;
std::string cpp_type_name() const override;
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_size_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
void generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &output,
const std::string &max_size = "size") const;
};
class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
public:
RepeatedPrimitiveFieldGenerator(const FieldDescriptor *descriptor,
const Options &options);
std::string cpp_type_name() const override;
void generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value = {},
bool can_ignore_default_value = true) const override;
void generate_calculate_packed_size_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_calculate_unpacked_size_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
void generate_deserialization_packed_only(google::protobuf::io::Printer *p,
const std::string &value,
const std::string &max_size) const;
void generate_deserialization_unpacked_only(google::protobuf::io::Printer *p,
const std::string &output) const;
private:
bool is_packed() const;
std::string packed_tag() const;
std::string unpacked_tag() const;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,185 +0,0 @@
#include "StringFieldGenerator.h"
namespace struct_pb {
namespace compiler {
StringFieldGenerator::StringFieldGenerator(const FieldDescriptor *field,
const Options &options)
: FieldGenerator(field, options) {}
void StringFieldGenerator::generate_calculate_size_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("$1$.size()", value);
}
void StringFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
auto tag_sz = calculate_tag_size(d_);
if (is_optional()) {
format("if ($1$.has_value()) {\n", value);
format.indent();
format("total += $1$ + calculate_varint_size($2$->size()) + $2$->size();\n",
tag_sz, value);
format.outdent();
format("}\n");
}
else {
if (can_ignore_default_value) {
format("if (!$1$.empty()) {\n", value);
format.indent();
format("total += $1$ + calculate_varint_size($2$.size()) + $2$.size();\n",
tag_sz, value);
format.outdent();
format("}\n");
}
else {
format("total += $1$ + calculate_varint_size($2$.size()) + $2$.size();\n",
tag_sz, value);
}
}
}
void StringFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
if (is_optional()) {
format("if ($1$.has_value()) {\n", value);
format.indent();
generate_serialization_only(p, value + ".value()");
format.outdent();
format("}\n");
}
else {
if (can_ignore_default_value) {
format("if (!$1$.empty()) {\n", value);
format.indent();
generate_serialization_only(p, value);
format.outdent();
format("}\n");
}
else {
generate_serialization_only(p, value);
}
}
}
void StringFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("serialize_varint(data, pos, size, $1$);\n", calculate_tag_str(d_));
format("serialize_varint(data, pos, size, $1$.size());\n", value);
format("std::memcpy(data + pos, $1$.data(), $1$.size());\n", value);
format("pos += $1$.size();\n", value);
}
std::string StringFieldGenerator::cpp_type_name() const {
if (d_->has_presence()) {
return "std::optional<std::string>";
}
return "std::string";
}
void StringFieldGenerator::generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &output,
const std::string &sz, const std::string &max_size) const {
p->Print({{"output", output}, {"sz", sz}, {"max_size", max_size}},
R"(uint64_t $sz$ = 0;
ok = deserialize_varint(data, pos, $max_size$, $sz$);
if (!ok) {
return false;
}
$output$.resize($sz$);
if (pos + $sz$ > $max_size$) {
return false;
}
std::memcpy($output$.data(), data+pos, $sz$);
pos += $sz$;
)");
}
void StringFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("case $1$: {\n", calculate_tag_str(d_));
format.indent();
if (is_optional()) {
format("if (!$1$.has_value()) {\n", value);
format.indent();
format("$1$ = std::string();\n", value);
format.outdent();
format("}\n");
generate_deserialization_only(p, value + ".value()");
}
else {
generate_deserialization_only(p, value);
}
format("break;\n");
format.outdent();
format("}\n");
}
RepeatedStringFieldGenerator::RepeatedStringFieldGenerator(
const FieldDescriptor *field, const Options &options)
: FieldGenerator(field, options) {}
std::string RepeatedStringFieldGenerator::cpp_type_name() const {
return "std::vector<std::string>";
}
void RepeatedStringFieldGenerator::generate_calculate_size(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
format("for (const auto& s: $1$) {\n", value);
format.indent();
format("total += $1$ + calculate_varint_size(s.size()) + s.size();\n",
calculate_tag_size(d_));
format.outdent();
format("}\n");
}
void RepeatedStringFieldGenerator::generate_calculate_only(
google::protobuf::io::Printer *p, const std::string &value,
const std::string &output) const {
p->Print({{"value", value},
{"tag_sz", calculate_tag_size(d_)},
{"output", output}},
R"(
std::size_t $output$ = 0;
for (const auto& s: $value$) {
$output$ += $tag_sz$ + calculate_varint_size(s.size()) + s.size();
}
)");
}
void RepeatedStringFieldGenerator::generate_serialization(
google::protobuf::io::Printer *p, const std::string &value,
bool can_ignore_default_value) const {
Formatter format(p);
if (can_ignore_default_value) {
format("if (!$1$.empty()) {\n", value);
format.indent();
generate_serialization_only(p, value);
format.outdent();
format("}\n");
}
else {
generate_serialization_only(p, value);
}
}
void RepeatedStringFieldGenerator::generate_serialization_only(
google::protobuf::io::Printer *p, const std::string &value) const {
Formatter format(p);
format("for(const auto& s: $1$) {\n", value);
format.indent();
StringFieldGenerator g(d_, options_);
g.generate_serialization_only(p, "s");
format.outdent();
format("}\n");
}
void RepeatedStringFieldGenerator::generate_deserialization(
google::protobuf::io::Printer *p, const std::string &value) const {
StringFieldGenerator g(d_, options_);
Formatter format(p);
format("case $1$: {\n", calculate_tag_str(d_));
format.indent();
format("std::string tmp_str;\n");
g.generate_deserialization_only(p, "tmp_str");
format("$1$.push_back(std::move(tmp_str));\n", value);
format("break;\n");
format.outdent();
format("}\n");
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,46 +0,0 @@
#pragma once
#include "FieldGenerator.h"
namespace struct_pb {
namespace compiler {
class StringFieldGenerator : public FieldGenerator {
public:
StringFieldGenerator(const FieldDescriptor *field, const Options &options);
std::string cpp_type_name() const override;
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_size_only(google::protobuf::io::Printer *p,
const std::string &value = {}) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
void generate_deserialization_only(
google::protobuf::io::Printer *p, const std::string &output,
const std::string &sz = "sz", const std::string &max_size = "size") const;
};
class RepeatedStringFieldGenerator : public FieldGenerator {
public:
RepeatedStringFieldGenerator(const FieldDescriptor *field,
const Options &options);
void generate_calculate_size(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_calculate_only(google::protobuf::io::Printer *p,
const std::string &value,
const std::string &output) const;
void generate_serialization(google::protobuf::io::Printer *p,
const std::string &value,
bool can_ignore_default_value) const override;
void generate_serialization_only(google::protobuf::io::Printer *p,
const std::string &value) const;
void generate_deserialization(google::protobuf::io::Printer *p,
const std::string &value) const override;
std::string cpp_type_name() const override;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,71 +0,0 @@
#include "StructGenerator.h"
#include <iostream>
#include "FileGenerator.h"
#include "Options.hpp"
#include "google/protobuf/descriptor.pb.h"
#include "helpers.hpp"
namespace struct_pb {
namespace compiler {
bool StructGenerator::Generate(
const google::protobuf::FileDescriptor *file, const std::string &parameter,
google::protobuf::compiler::GeneratorContext *generator_context,
std::string *error) const {
std::vector<std::pair<std::string, std::string>> options;
google::protobuf::compiler::ParseGeneratorParameter(parameter, &options);
Options struct_pb_options(file);
for (const auto &option : options) {
const auto &key = option.first;
const auto &value = option.second;
if (key == "generate_eq_op") {
struct_pb_options.generate_eq_op = true;
}
else if (key == "namespace") {
struct_pb_options.ns = value;
}
else {
*error = "Unknown generator option: " + key;
return false;
}
}
if (struct_pb_options.ns.empty()) {
struct_pb_options.ns = file->package();
}
auto basename = strip_proto(file->name());
FileGenerator file_generator(file, struct_pb_options);
// generate xxx.struct_pb.h
{
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".struct_pb.h"));
google::protobuf::io::Printer p(output.get(), '$');
p.Print({{"parameter", parameter}}, R"(// protoc generate parameter
// clang-format off
// $parameter$
// =========================
#pragma once
)");
file_generator.generate_header(&p);
}
// generate xxx.struct_pb.cc
{
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".struct_pb.cc"));
google::protobuf::io::Printer p(output.get(), '$');
p.Print(
{{"parameter", parameter}, {"header_file", basename + ".struct_pb.h"}},
R"(// protoc generate parameter
// clang-format off
// $parameter$
// =========================
#include "$header_file$"
#include "ylt/struct_pb/struct_pb_impl.hpp"
)");
Formatter format(&p);
file_generator.generate_source(&p);
}
return true;
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,15 +0,0 @@
#pragma once
#include <google/protobuf/compiler/code_generator.h>
namespace struct_pb {
namespace compiler {
class StructGenerator : public google::protobuf::compiler::CodeGenerator {
public:
bool Generate(const google::protobuf::FileDescriptor *file,
const std::string &parameter,
google::protobuf::compiler::GeneratorContext *generator_context,
std::string *error) const override;
};
} // namespace compiler
} // namespace struct_pb

View File

@ -1,67 +0,0 @@
#include "helpers.hpp"
#include <algorithm>
#include <sstream>
#include "google/protobuf/io/printer.h"
namespace struct_pb {
namespace compiler {
// https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
template <typename Out>
void split(const std::string &s, char delim, Out result) {
std::istringstream iss(s);
std::string item;
while (std::getline(iss, item, delim)) {
*result++ = item;
}
}
static std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, std::back_inserter(elems));
return elems;
}
NamespaceOpener::NamespaceOpener(google::protobuf::io::Printer *p,
const std::string &package)
: p_(p) {
ns_ = split(package, '.');
}
NamespaceOpener::~NamespaceOpener() {}
void NamespaceOpener::open() const {
for (const auto &ns : ns_) {
p_->Print({{"ns", ns}}, R"(
namespace $ns$ {
)");
}
}
void NamespaceOpener::close() const {
for (int i = 0; i < ns_.size(); ++i) {
auto ns = ns_[ns_.size() - i - 1];
p_->Print({{"ns", ns}}, R"(
} // namespace $ns$
)");
}
}
// from std::string_view ends_with
bool string_ends_with(std::string_view s, std::string_view suffix) {
return s.size() >= suffix.size() &&
s.compare(s.size() - suffix.size(), std::string_view::npos, suffix) ==
0;
}
std::string_view string_strip_suffix(const std::string_view s,
const std::string_view suffix) {
if (string_ends_with(s, suffix)) {
return s.substr(0, s.size() - suffix.size());
}
return s;
}
std::string strip_proto(const std::string &filename) {
if (string_ends_with(filename, ".protodevel")) {
return std::string(string_strip_suffix(filename, ".protodevel"));
}
else {
return std::string(string_strip_suffix(filename, ".proto"));
}
}
} // namespace compiler
} // namespace struct_pb

View File

@ -1,279 +0,0 @@
#pragma once
#include <string>
#include <string_view>
#include <unordered_map>
#include "Options.hpp"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
namespace struct_pb {
namespace compiler {
using namespace google::protobuf;
using namespace google::protobuf::compiler;
inline bool is_map_entry(const google::protobuf::Descriptor *descriptor) {
return descriptor->options().map_entry();
}
void flatten_messages_in_file(
const google::protobuf::FileDescriptor *file,
std::vector<const google::protobuf::Descriptor *> *result);
inline std::vector<const google::protobuf::Descriptor *>
flatten_messages_in_file(const google::protobuf::FileDescriptor *file) {
std::vector<const google::protobuf::Descriptor *> ret;
flatten_messages_in_file(file, &ret);
return ret;
}
template <typename F>
void for_each_message(const google::protobuf::Descriptor *descriptor,
F &&func) {
for (int i = 0; i < descriptor->nested_type_count(); ++i) {
for_each_message(descriptor->nested_type(i), std::forward<F &&>(func));
}
func(descriptor);
}
template <typename F>
void for_each_message(const google::protobuf::FileDescriptor *descriptor,
F &&func) {
for (int i = 0; i < descriptor->message_type_count(); ++i) {
for_each_message(descriptor->message_type(i), std::forward<F &&>(func));
}
}
inline void flatten_messages_in_file(
const google::protobuf::FileDescriptor *file,
std::vector<const google::protobuf::Descriptor *> *result) {
for (int i = 0; i < file->message_type_count(); ++i) {
for_each_message(file->message_type(i),
[&](const google::protobuf::Descriptor *descriptor) {
if (is_map_entry(descriptor)) {
return;
}
result->push_back(descriptor);
});
}
}
inline std::string resolve_keyword(const std::string &name) {
// clang-format off
static std::set<std::string> keyword_set{
//
"NULL",
"alignas",
"alignof",
"and",
"and_eq",
"asm",
"auto",
"bitand",
"bitor",
"bool",
"break",
"case",
"catch",
"char",
"class",
"compl",
"const",
"constexpr",
"const_cast",
"continue",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
"mutable",
"namespace",
"new",
"noexcept",
"not",
"not_eq",
"nullptr",
"operator",
"or",
"or_eq",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
"xor",
"xor_eq",
"char8_t",
"char16_t",
"char32_t",
"concept",
"consteval",
"constinit",
"co_await",
"co_return",
"co_yield",
"requires",
// typedef
"uint32_t",
"int32_t",
"uint64_t",
"int64_t",
"size_t",
"memset"
};
// clang-format on
auto it = keyword_set.find(name);
if (it == keyword_set.end()) {
return name;
}
return name + "_";
}
inline std::string class_name(const google::protobuf::Descriptor *descriptor) {
// assert(descriptor);
auto parent = descriptor->containing_type();
std::string ret;
if (parent) {
ret += class_name(parent) + "::";
}
ret += resolve_keyword(descriptor->name());
return resolve_keyword(ret);
}
inline std::string enum_name(
const google::protobuf::EnumDescriptor *descriptor) {
auto parent = descriptor->containing_type();
std::string ret;
if (parent) {
ret += class_name(parent) + "::";
}
ret += resolve_keyword(descriptor->name());
return resolve_keyword(ret);
}
// https://stackoverflow.com/questions/2896600/how-to-replace-all-occurrences-of-a-character-in-string
inline std::string ReplaceAll(std::string str, const std::string &from,
const std::string &to) {
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos +=
to.length(); // Handles case where 'to' is a substring of 'from'
}
return str;
}
inline std::string dots_to_colons(std::string name) {
return ReplaceAll(name, ".", "::");
}
inline std::string get_namespace(const std::string &package) {
if (package.empty()) {
return "";
}
return "::" + dots_to_colons(package);
}
inline std::string get_namespace(const google::protobuf::FileDescriptor *file,
const Options &options) {
if (file == options.f) {
return get_namespace(options.ns);
}
else {
return get_namespace(file->package());
}
}
inline std::string qualified_file_level_symbol(
const google::protobuf::FileDescriptor *file, const std::string &name,
const Options &options) {
// if (options.ns.empty()) {
// return absl::StrCat("::", name);
// }
return get_namespace(file, options) + "::" + name;
}
inline std::string qualified_class_name(const google::protobuf::Descriptor *d,
const Options &options) {
return qualified_file_level_symbol(d->file(), class_name(d), options);
}
inline std::string qualified_enum_name(
const google::protobuf::EnumDescriptor *d, const Options &options) {
return qualified_file_level_symbol(d->file(), enum_name(d), options);
}
class Formatter {
public:
Formatter(google::protobuf::io::Printer *printer) : printer_(printer) {}
void indent() const { printer_->Indent(); }
void outdent() const { printer_->Outdent(); }
template <typename... Args>
void operator()(const char *format, const Args &...args) const {
printer_->FormatInternal({ToString(args)...}, vars_, format);
}
private:
static std::string ToString(const std::string &s) { return s; }
template <typename I, typename = typename std::enable_if<
std::is_integral<I>::value>::type>
static std::string ToString(I x) {
return std::to_string(x);
}
private:
google::protobuf::io::Printer *printer_;
std::map<std::string, std::string> vars_;
};
class NamespaceOpener {
public:
NamespaceOpener(google::protobuf::io::Printer *p,
const std::string &package = {});
~NamespaceOpener();
public:
void open() const;
void close() const;
private:
std::vector<std::string> ns_;
google::protobuf::io::Printer *p_;
};
std::string strip_proto(const std::string &filename);
} // namespace compiler
} // namespace struct_pb

View File

@ -1,9 +0,0 @@
#include <google/protobuf/compiler/plugin.h>
#include "StructGenerator.h"
using namespace struct_pb::compiler;
using namespace google::protobuf::compiler;
int main(int argc, char* argv[]) {
StructGenerator generator;
return PluginMain(argc, argv, &generator);
}

View File

@ -1,55 +1,39 @@
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests)
find_package(Protobuf QUIET) find_package(Protobuf QUIET)
if("${yaLanTingLibs_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
# If this is a subproject in ylt
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests)
else()
# else find installed yalantinglibs
cmake_minimum_required(VERSION 3.15)
project(struct_pb_test)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# if you have install ylt
find_package(yalantinglibs REQUIRED)
link_libraries(yalantinglibs::yalantinglibs)
# else
# include_directories(include)
# include_directories(include/ylt/thirdparty)
endif()
set(TEST_PROTO main.cpp)
if (Protobuf_FOUND) if (Protobuf_FOUND)
add_executable(test_struct_pb add_definitions(-DSTRUCT_PB_WITH_PROTO)
test_pb.cpp message(STATUS "Found Protobuf: ${Protobuf_VERSION}")
test_pb_oneof.cpp include_directories(${Protobuf_INCLUDE_DIRS})
test_pb_benchmark_struct.cpp include_directories(${CMAKE_CURRENT_BINARY_DIR})
test_pb_bad_identifiers.cpp set(PROTO_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/)
) file(GLOB PROTO_FILES ${PROTO_SRC_DIR}/*.proto)
target_sources(test_struct_pb PRIVATE
main.cpp protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES})
) # message(STATUS "Proto source files: ${PROTO_SRCS}")
add_test(NAME test_struct_pb COMMAND test_struct_pb) # message(STATUS "Proto header files: ${PROTO_HDRS}")
target_include_directories(test_struct_pb PUBLIC ${yaLanTingLibs_SOURCE_DIR}/src/struct_pack/benchmark) add_executable(struct_pb_test ${PROTO_SRCS} ${TEST_PROTO})
target_compile_definitions(test_struct_pb PRIVATE HAVE_PROTOBUF) target_link_libraries(struct_pb_test PRIVATE protobuf::libprotobuf)
target_link_libraries(test_struct_pb PRIVATE protobuf::libprotobuf) add_test(NAME struct_pb_test COMMAND struct_pb_test)
# generate .pb.cc .pb.h else()
protobuf_generate_cpp(PROTO_SRCS add_executable(struct_pb_test ${TEST_PROTO})
PROTO_HDRS add_test(NAME struct_pb_test COMMAND struct_pb_test)
test_pb.proto endif()
data_def.proto
)
target_include_directories(test_struct_pb PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_sources(test_struct_pb PRIVATE
${PROTO_SRCS}
${PROTO_HDRS}
)
protobuf_generate_struct_pb(STRUCT_PB_PROTO_SRCS
STRUCT_PB_PROTO_HDRS
test_pb.proto
OPTION "generate_eq_op=true,namespace=test_struct_pb"
)
target_sources(test_struct_pb PRIVATE
${STRUCT_PB_PROTO_SRCS}
${STRUCT_PB_PROTO_HDRS}
)
protobuf_generate_struct_pb(STRUCT_PB_TEST_BENCHMARK_PROTO_SRCS
STRUCT_PB_TEST_BENCHMARK_PROTO_HDRS
data_def.proto
OPTION "namespace=struct_pb_sample"
)
target_sources(test_struct_pb PRIVATE
${STRUCT_PB_TEST_BENCHMARK_PROTO_SRCS}
${STRUCT_PB_TEST_BENCHMARK_PROTO_HDRS}
)
protobuf_generate_struct_pb(STRUCT_PB_PROTO_SRCS2
STRUCT_PB_PROTO_HDRS2
test_bad_identifiers.proto
test_large_enum_value.proto
)
target_sources(test_struct_pb PRIVATE
${STRUCT_PB_PROTO_SRCS2}
${STRUCT_PB_PROTO_HDRS2}
)
endif ()

View File

@ -37,17 +37,6 @@ message Monsters {
repeated Monster monsters = 1; repeated Monster monsters = 1;
} }
message rect32 {
int32 x = 1;
int32 y = 2;
int32 width = 3;
int32 height = 4;
}
message rect32s {
repeated rect32 rect32_list = 1;
}
message person { message person {
int32 id = 1; int32 id = 1;
string name = 2; string name = 2;
@ -57,4 +46,11 @@ message person {
message persons { message persons {
repeated person person_list = 1; repeated person person_list = 1;
}
message bench_int32 {
int32 a = 1;
int32 b = 2;
int32 c = 3;
int32 d = 4;
} }

View File

@ -1,78 +0,0 @@
#pragma once
#include <optional>
#include "data_def.struct_pb.h"
#include "hex_printer.hpp"
#include "test_pb.struct_pb.h"
#include "ylt/struct_pb.hpp"
#ifdef HAVE_PROTOBUF
#include "google/protobuf/repeated_field.h"
#endif
template <typename T, typename Pred = std::equal_to<T>>
void check_self(const T& t, std::optional<std::string> buf_opt = {}) {
auto size = struct_pb::internal::get_needed_size(t);
if (buf_opt.has_value()) {
REQUIRE(buf_opt.value().size() == size);
}
auto b = struct_pb::serialize<std::string>(t);
if (buf_opt.has_value()) {
CHECK(hex_helper(buf_opt.value()) == hex_helper(b));
}
T d_t{};
auto ok = struct_pb::deserialize_to(d_t, b);
REQUIRE(ok);
CHECK(Pred()(t, d_t));
}
#ifdef HAVE_PROTOBUF
template <typename T, typename PB_T>
struct PB_equal {
constexpr bool operator()(const T& t, const PB_T& pb_t) const {
return t == pb_t;
}
};
template <typename T, typename PB_T, typename Pred = PB_equal<T, PB_T>>
void check_with_protobuf(const T& t, const PB_T& pb_t) {
auto pb_buf = pb_t.SerializeAsString();
auto size = struct_pb::internal::get_needed_size(t);
REQUIRE(size == pb_buf.size());
std::string b = struct_pb::serialize<std::string>(t);
CHECK(hex_helper(b) == hex_helper(pb_buf));
CHECK(Pred()(t, pb_t));
T d_t{};
auto ok = struct_pb::deserialize_to(d_t, b);
REQUIRE(ok);
CHECK(Pred()(d_t, pb_t));
}
template <typename T, typename U>
bool operator==(const std::vector<T>& a,
const google::protobuf::RepeatedPtrField<U>& b) {
if (a.size() != b.size()) {
return false;
}
auto sz = a.size();
for (int i = 0; i < sz; ++i) {
if (!PB_equal<T, U>()(a[i], b.at(i))) {
return false;
}
}
return true;
}
template <typename T, typename U>
bool operator==(const std::vector<T>& a,
const google::protobuf::RepeatedField<U>& b) {
if (a.size() != b.size()) {
return false;
}
auto sz = a.size();
for (int i = 0; i < sz; ++i) {
if (a[i] != b.at(i)) {
return false;
}
}
return true;
}
#endif

View File

@ -1,64 +0,0 @@
/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <iostream>
#include <string>
// useful for debugging
inline void print_hex(uint8_t v) {
static std::string hex = "0123456789abcedf";
if (v < 16) {
std::cout << "0";
std::cout << hex[v];
}
else {
std::size_t first = v / 16;
std::size_t last = v % 16;
std::cout << hex[first] << hex[last];
}
}
inline void print_hex(std::string_view sv) {
for (char ch : sv) {
uint8_t v = static_cast<uint8_t>(ch);
print_hex(v);
std::cout << " ";
}
std::cout << std::endl;
}
struct hex_helper {
hex_helper(std::string_view sv) : sv(sv) {}
friend std::ostream& operator<<(std::ostream& os, const hex_helper& helper) {
static std::string hex = "0123456789abcedf";
auto to_hex = [&os](std::string_view hex, uint8_t v) {
if (v < 16) {
os << "0";
os << hex[v];
}
else {
std::size_t first = v / 16;
std::size_t last = v % 16;
os << hex[first] << hex[last];
}
};
for (char ch : helper.sv) {
uint8_t v = static_cast<uint8_t>(ch);
to_hex(hex, v);
os << " ";
}
return os;
}
bool operator==(const hex_helper&) const = default;
std::string_view sv;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,191 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file tests that various identifiers work as field and type names even
// though the same identifiers are used internally by the C++ code generator.
// LINT: LEGACY_NAMES
syntax = "proto2";
// Some generic_services option(s) added automatically.
// See: http://go/proto2-generic-services-default
option cc_generic_services = true; // auto-added
// 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.
package protobuf_unittest;
// Test that fields can have names like "input" and "i" which are also used
// internally by the code generator for local variables.
message TestConflictingSymbolNames {
message BuildDescriptors {}
message TypeTraits {}
optional int32 input = 1;
optional int32 output = 2;
optional string length = 3;
repeated int32 i = 4;
repeated string new_element = 5 [ctype = STRING_PIECE];
optional int32 total_size = 6;
optional int32 tag = 7;
enum TestEnum {
FOO = 0;
}
message Data1 {
repeated int32 data = 1;
}
message Data2 {
repeated TestEnum data = 1;
}
message Data3 {
repeated string data = 1;
}
message Data4 {
repeated Data4 data = 1;
}
message Data5 {
repeated string data = 1 [ctype = STRING_PIECE];
}
message Data6 {
repeated string data = 1 [ctype = CORD];
}
optional int32 source = 8;
optional int32 value = 9;
optional int32 file = 10;
optional int32 from = 11;
optional int32 handle_uninterpreted = 12;
repeated int32 index = 13;
optional int32 controller = 14;
optional int32 already_here = 15;
optional uint32 uint32 = 16;
optional uint32 uint32_t = 41;
optional uint64 uint64 = 17;
optional uint32 uint64_t = 42;
optional string string = 18;
optional int32 memset = 19;
optional int32 int32 = 20;
optional int32 int32_t = 43;
optional int64 int64 = 21;
optional int64 int64_t = 44;
optional int64 size_t = 45;
optional uint32 cached_size = 22;
optional uint32 extensions = 23;
optional uint32 bit = 24;
optional uint32 bits = 25;
optional uint32 offsets = 26;
optional uint32 reflection = 27;
message Cord {}
optional string some_cord = 28 [ctype = CORD];
message StringPiece {}
optional string some_string_piece = 29 [ctype = STRING_PIECE];
// Some keywords.
optional uint32 int = 30;
optional uint32 friend = 31;
optional uint32 class = 37;
optional uint32 typedecl = 39;
optional uint32 auto = 40;
// The generator used to #define a macro called "DO" inside the .cc file.
message DO {}
optional DO do = 32;
// Some template parameter names for extensions.
optional int32 field_type = 33;
optional bool is_packed = 34;
// test conflicting release_$name$. "length" and "do" field in this message
// must remain string or message fields to make the test valid.
optional string release_length = 35;
// A more extreme case, the field name "do" here is a keyword, which will be
// escaped to "do_" already. Test there is no conflict even with escaped field
// names.
optional DO release_do = 36;
// For clashing local variables in Serialize and ByteSize calculation.
optional string target = 38;
extensions 1000 to max; // NO_PROTO3
}
message TestConflictingSymbolNamesExtension { // NO_PROTO3
extend TestConflictingSymbolNames { // NO_PROTO3
repeated int32 repeated_int32_ext = 20423638 [packed = true]; // NO_PROTO3
} // NO_PROTO3
} // NO_PROTO3
message TestConflictingEnumNames { // NO_PROTO3
enum while { // NO_PROTO3
default = 0; // NO_PROTO3
and = 1; // NO_PROTO3
class = 2; // NO_PROTO3
int = 3; // NO_PROTO3
typedef = 4; // NO_PROTO3
XOR = 5; // NO_PROTO3
} // NO_PROTO3
optional while conflicting_enum = 1; // NO_PROTO3
} // NO_PROTO3
enum bool { // NO_PROTO3
default = 0; // NO_PROTO3
NOT_EQ = 1; // NO_PROTO3
volatile = 2; // NO_PROTO3
return = 3; // NO_PROTO3
} // NO_PROTO3
message DummyMessage {}
message NULL {
optional int32 int = 1;
}
extend TestConflictingSymbolNames { // NO_PROTO3
optional int32 void = 314253; // NO_PROTO3
} // NO_PROTO3
// Message names that could conflict.
message Shutdown {}
message TableStruct {}
service TestConflictingMethodNames {
rpc Closure(DummyMessage) returns (DummyMessage);
}

View File

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

View File

@ -1,851 +0,0 @@
/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <valarray>
#include "doctest.h"
#include "helper.hpp"
#ifdef HAVE_PROTOBUF
#include "test_pb.pb.h"
#endif
using namespace doctest;
using namespace struct_pb;
TEST_SUITE_BEGIN("test pb");
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::Test1, Test1> {
bool operator()(const test_struct_pb::Test1& t, const Test1& pb_t) const {
return t.a == pb_t.a();
}
};
#endif
TEST_CASE("testing test_struct_pb::Test1") {
test_struct_pb::Test1 t{};
SUBCASE("empty") { check_self(t); }
SUBCASE("has value") {
std::string buf{0x08, (char)0x96, 0x01};
t.a = 150;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
Test1 pb_t;
pb_t.set_a(150);
auto pb_buf = pb_t.SerializeAsString();
REQUIRE(pb_buf == buf);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("negative") {
{
// an example for check arm char
std::string buf{0x08, (char)0xff, (char)0xff, (char)0xff,
(char)0xff, (char)0xff, (char)0xff, (char)0xff,
(char)0xff, (char)0xff, 0x01};
test_struct_pb::Test1 t{.a = -1};
check_self(t, buf);
#ifdef HAVE_PROTOBUF
// for debug
Test1 pb_t;
pb_t.set_a(-1);
auto s = pb_t.SerializeAsString();
check_with_protobuf(t, pb_t);
#endif
}
for (int32_t i = -1; i > INT16_MIN + 1; i *= 2) {
test_struct_pb::Test1 t{.a = i};
check_self(t);
#ifdef HAVE_PROTOBUF
Test1 pb_t;
pb_t.set_a(i);
check_with_protobuf(t, pb_t);
#endif
}
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::Test2, Test2> {
bool operator()(const test_struct_pb::Test2& t, const Test2& pb_t) const {
return t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing test_struct_pb::Test2") {
test_struct_pb::Test2 t{};
SUBCASE("empty") { check_self(t); }
SUBCASE("has value") {
std::string buf{0x12, 0x07};
std::string s = "testing";
buf += s;
t.b = s;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
Test2 pb_t;
pb_t.set_b(s);
check_with_protobuf(t, pb_t);
#endif
}
}
namespace std {
template <>
struct equal_to<test_struct_pb::Test3> {
bool operator()(const test_struct_pb::Test3& lhs,
const test_struct_pb::Test3& rhs) const {
if (lhs.c && rhs.c) {
return equal_to<test_struct_pb::Test1>()(*lhs.c, *rhs.c);
}
if (!lhs.c && !rhs.c) {
return true;
}
return false;
}
};
} // namespace std
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::Test3, Test3> {
bool operator()(const test_struct_pb::Test3& t, const Test3& pb_t) const {
if (t.c && pb_t.has_c()) {
return PB_equal<test_struct_pb::Test1, Test1>()(*t.c, pb_t.c());
}
if (!t.c && !pb_t.has_c()) {
return true;
}
return false;
}
};
#endif
TEST_CASE("testing test_struct_pb::Test3") {
test_struct_pb::Test3 t{};
SUBCASE("empty") { check_self(t); }
SUBCASE("has value") {
std::string buf{0x1a, 0x03, 0x08, (char)0x96, 0x01};
t.c = std::make_unique<test_struct_pb::Test1>(
test_struct_pb::Test1{.a = 150});
check_self(t, buf);
#ifdef HAVE_PROTOBUF
Test3 pb_t;
auto pb_c = new Test1;
pb_c->set_a(150);
pb_t.set_allocated_c(pb_c);
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::Test4, Test4> {
bool operator()(const test_struct_pb::Test4& t, const Test4& pb_t) const {
return t.e == pb_t.e();
}
};
#endif
TEST_CASE("testing test_struct_pb::Test4") {
test_struct_pb::Test4 t{};
SUBCASE("empty") { check_self(t); }
SUBCASE("string empty") {
std::string buf{0x2a, 0x01, 0x01};
t.e = {1};
check_self(t, buf);
#ifdef HAVE_PROTOBUF
Test4 pb_t;
pb_t.add_e(1);
auto pb_buf = pb_t.SerializeAsString();
REQUIRE(pb_buf == buf);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("repeated empty") {
// [4] 22 2 68 69
std::string buf{0x22, 0x02, 0x68, 0x69};
std::string s = "hi";
t.d = s;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
Test4 pb_t;
pb_t.set_d(s);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("has value") {
std::string buf{0x22, 0x05, 0x68, 0x65, 0x6c, 0x6c,
0x6f, 0x2a, 0x03, 0x01, 0x02, 0x03};
// why document write
// 220568656c6c6f280128022803
// while my pb c++ code generated
// 34 5 'h' 'e' 'l' 'l' 'o' 42 3 1 2 3
// 22 5 68 65 6c 6c 6f 2a 3 1 2 3
t.d = "hello";
t.e = {1, 2, 3};
check_self(t, buf);
#ifdef HAVE_PROTOBUF
Test4 pb_t;
pb_t.set_d("hello");
pb_t.add_e(1);
pb_t.add_e(2);
pb_t.add_e(3);
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestDouble, MyTestDouble> {
bool operator()(const test_struct_pb::MyTestDouble& t,
const MyTestDouble& pb_t) const {
std::valarray<double> ts{t.a, t.b, t.c};
std::valarray<double> pb_ts{pb_t.a(), pb_t.b(), pb_t.c()};
return (std::abs(ts - pb_ts) < 0.00005f).min();
}
};
#endif
TEST_CASE("testing double") {
test_struct_pb::MyTestDouble t{.a = 123.456, .b = 0, .c = -678.123};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestDouble pb_t;
pb_t.set_a(t.a);
pb_t.set_b(t.b);
pb_t.set_c(t.c);
check_with_protobuf(t, pb_t);
#endif
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestFloat, MyTestFloat> {
bool operator()(const test_struct_pb::MyTestFloat& t,
const MyTestFloat& pb_t) const {
std::valarray<float> ts{t.a, t.b, t.c, t.d, t.e};
std::valarray<float> pb_ts{pb_t.a(), pb_t.b(), pb_t.c(), pb_t.d(),
pb_t.e()};
return (std::abs(ts - pb_ts) < 0.00005f).min();
}
};
#endif
TEST_CASE("testing float") {
// [20] 15 00 00 80 3f 1d 00 00 80 bf 25 b6 f3 9d 3f 2d 00 04 f1 47
std::string buf{0x15, 0x00, 0x00, (char)0x80, 0x3f,
0x1d, 0x00, 0x00, (char)0x80, (char)0xbf,
0x25, (char)0xb6, (char)0xf3, (char)0x9d, 0x3f,
0x2d, 0x00, 0x04, (char)0xf1, 0x47};
test_struct_pb::MyTestFloat t{
.a = 0, .b = 1, .c = -1, .d = 1.234, .e = 1.234e5};
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestFloat pb_t;
pb_t.set_a(0);
pb_t.set_b(1);
pb_t.set_c(-1);
pb_t.set_d(1.234);
pb_t.set_e(1.234e5);
check_with_protobuf(t, pb_t);
#endif
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestInt32, MyTestInt32> {
bool operator()(const test_struct_pb::MyTestInt32& t,
const MyTestInt32& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing int32") {
std::string buf{0x08, (char)0x80, 0x01};
test_struct_pb::MyTestInt32 t;
t.a = 128;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestInt32 pb_t;
pb_t.set_a(128);
check_with_protobuf(t, pb_t);
#endif
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestInt64, MyTestInt64> {
bool operator()(const test_struct_pb::MyTestInt64& t,
const MyTestInt64& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing int64") {
int64_t max_val = 4;
max_val *= INT32_MAX;
for (int64_t i = 1; i < max_val; i *= -2) {
test_struct_pb::MyTestInt64 t{.a = i};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestInt64 pb_t;
pb_t.set_a(i);
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestUint32, MyTestUint32> {
bool operator()(const test_struct_pb::MyTestUint32& t,
const MyTestUint32& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing uint32") {
int32_t max_val = 4;
max_val *= INT16_MAX;
for (uint32_t i = 1; i < max_val; i *= 2) {
test_struct_pb::MyTestUint32 t{.a = i};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestUint32 pb_t;
pb_t.set_a(i);
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestUint64, MyTestUint64> {
bool operator()(const test_struct_pb::MyTestUint64& t,
const MyTestUint64& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing uint64") {
int64_t max_val = 4;
max_val *= INT32_MAX;
for (uint64_t i = 1; i < max_val; i *= 2) {
test_struct_pb::MyTestUint64 t{.a = i};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestUint64 pb_t;
pb_t.set_a(i);
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestEnum, MyTestEnum> {
bool operator()(const test_struct_pb::MyTestEnum& t,
const MyTestEnum& pb_t) const {
return static_cast<uint32_t>(t.color) ==
static_cast<uint32_t>(pb_t.color());
}
};
#endif
TEST_CASE("testing enum") {
SUBCASE("Red") {
std::string buf;
test_struct_pb::MyTestEnum t{};
t.color = test_struct_pb::MyTestEnum::Color::Red;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestEnum pb_t;
pb_t.set_color(MyTestEnum_Color::MyTestEnum_Color_Red);
auto pb_buf = pb_t.SerializeAsString();
REQUIRE(hex_helper(pb_buf) == hex_helper(buf));
MyTestEnum pb_d_t;
auto ok = pb_d_t.ParseFromArray(pb_buf.data(), pb_buf.size());
REQUIRE(ok);
CHECK(pb_d_t.IsInitialized());
CHECK(pb_d_t.color() == pb_t.color());
auto c = pb_d_t.color();
CHECK(c == MyTestEnum_Color::MyTestEnum_Color_Red);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("Green") {
std::string buf{0x30, 0x01};
test_struct_pb::MyTestEnum t{};
t.color = test_struct_pb::MyTestEnum::Color::Green;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestEnum pb_t;
pb_t.set_color(MyTestEnum_Color::MyTestEnum_Color_Green);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("Blue") {
std::string buf{0x30, 0x02};
test_struct_pb::MyTestEnum t{};
t.color = test_struct_pb::MyTestEnum::Color::Blue;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestEnum pb_t;
pb_t.set_color(MyTestEnum_Color::MyTestEnum_Color_Blue);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("Enum127") {
std::string buf{0x30, (char)0x7f};
test_struct_pb::MyTestEnum t{};
t.color = test_struct_pb::MyTestEnum::Color::Enum127;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestEnum pb_t;
pb_t.set_color(MyTestEnum_Color::MyTestEnum_Color_Enum127);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("Enum128") {
std::string buf{0x30, (char)0x80, 0x01};
test_struct_pb::MyTestEnum t{};
t.color = test_struct_pb::MyTestEnum::Color::Enum128;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestEnum pb_t;
pb_t.set_color(MyTestEnum_Color::MyTestEnum_Color_Enum128);
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestRepeatedMessage, MyTestRepeatedMessage> {
bool operator()(const test_struct_pb::MyTestRepeatedMessage& t,
const MyTestRepeatedMessage& pb_t) const {
return t.fs == pb_t.fs();
}
};
#endif
TEST_CASE("testing nested repeated message") {
SUBCASE("one") {
test_struct_pb::MyTestRepeatedMessage t{};
t.fs = {{1, 2, 3}};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestRepeatedMessage pb_t;
{
auto f1 = pb_t.add_fs();
f1->set_a(1);
f1->set_b(2);
f1->set_c(3);
}
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("two") {
test_struct_pb::MyTestRepeatedMessage t{};
t.fs = {{1, 2, 3}, {4, 5, 6}};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestRepeatedMessage pb_t;
{
auto f1 = pb_t.add_fs();
f1->set_a(1);
f1->set_b(2);
f1->set_c(3);
}
{
auto f1 = pb_t.add_fs();
f1->set_a(4);
f1->set_b(5);
f1->set_c(6);
}
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestSint32, MyTestSint32> {
bool operator()(const test_struct_pb::MyTestSint32& t,
const MyTestSint32& pb_t) const {
return t.a == pb_t.a();
}
};
#endif
TEST_CASE("testing sint32") {
SUBCASE("-1") {
std::string buf{0x08, 0x01};
test_struct_pb::MyTestSint32 t{};
t.a = -1;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestSint32 pb_t;
pb_t.set_a(-1);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("1") {
test_struct_pb::MyTestSint32 t{};
t.a = 1;
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestSint32 pb_t;
pb_t.set_a(1);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("128") {
std::string buf{0x08, (char)0x80, 0x02};
test_struct_pb::MyTestSint32 t{};
t.a = 128;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestSint32 pb_t;
pb_t.set_a(128);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("range") {
for (int32_t i = INT16_MAX; i > INT16_MIN + 1; --i) {
test_struct_pb::MyTestSint32 t{.a = i};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestSint32 pb_t;
pb_t.set_a(i);
check_with_protobuf(t, pb_t);
#endif
}
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestSint64, MyTestSint64> {
bool operator()(const test_struct_pb::MyTestSint64& t,
const MyTestSint64& pb_t) const {
return t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing sint64") {
SUBCASE("-1") {
std::string buf{0x10, 0x01};
test_struct_pb::MyTestSint64 t{};
t.b = -1;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestSint64 pb_t;
pb_t.set_b(-1);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("128") {
std::string buf{0x10, (char)0x80, 0x02};
test_struct_pb::MyTestSint64 t{};
t.b = 128;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestSint64 pb_t;
pb_t.set_b(128);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("range") {
for (int64_t i = -1; i > INT32_MIN + 1; i *= -2) {
test_struct_pb::MyTestSint64 t{.b = i};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestSint64 pb_t;
pb_t.set_b(i);
check_with_protobuf(t, pb_t);
#endif
}
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestMap, MyTestMap> {
bool operator()(const test_struct_pb::MyTestMap& t,
const MyTestMap& pb_t) const {
if (t.e.size() != pb_t.e_size()) {
return false;
}
for (const auto& [k, v] : pb_t.e()) {
auto it = t.e.find(k);
if (it == t.e.end()) {
return false;
}
if (it->second != v) {
return false;
}
}
return true;
}
};
#endif
TEST_CASE("testing map") {
SUBCASE("one entry") {
std::string buf{0x1a, 0x05, 0x0a, 0x01, 0x61, 0x10, 0x01};
test_struct_pb::MyTestMap t;
t.e["a"] = 1;
check_self(t, buf);
#ifdef HAVE_PROTOBUF
MyTestMap pb_t;
auto& pb_m = *pb_t.mutable_e();
pb_m["a"] = 1;
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("two entry") {
std::string buf{
0x1a, 0x0a, 0x0a, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x10,
(char)0x9a, 0x05, 0x1a, 0x05, 0x0a, 0x01, 0x61, 0x10, 0x01,
};
std::string buf2{
0x1a, 0x05, 0x0a, 0x01, 0x61, 0x10, 0x01, 0x1a, 0x0a, 0x0a,
0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x10, (char)0x9a, 0x05,
};
test_struct_pb::MyTestMap t;
t.e["a"] = 1;
t.e["hello"] = 666;
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestMap pb_t;
auto& pb_m = *pb_t.mutable_e();
pb_m["a"] = 1;
pb_m["hello"] = 666;
auto pb_buf = pb_t.SerializeAsString();
CHECK(buf.size() == pb_buf.size());
REQUIRE((buf == pb_buf || buf2 == pb_buf));
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestFixed32, MyTestFixed32> {
bool operator()(const test_struct_pb::MyTestFixed32& t,
const MyTestFixed32& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing fixed32") {
test_struct_pb::MyTestFixed32 t{};
SUBCASE("single fixed") {
t.a = 888;
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestFixed32 pb_t;
pb_t.set_a(888);
check_with_protobuf(t, pb_t);
#endif
}
SUBCASE("only repeated") {
t.b = {5, 4, 3, 2, 1};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestFixed32 pb_t;
pb_t.add_b(5);
pb_t.add_b(4);
pb_t.add_b(3);
pb_t.add_b(2);
pb_t.add_b(1);
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestFixed64, MyTestFixed64> {
bool operator()(const test_struct_pb::MyTestFixed64& t,
const MyTestFixed64& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing fixed64") {
using T = uint64_t;
T max_val = 4;
max_val *= INT32_MAX;
for (T i = 1; i < max_val; i *= 2) {
test_struct_pb::MyTestFixed64 t{.a = i};
#ifdef HAVE_PROTOBUF
MyTestFixed64 pb_t;
pb_t.set_a(i);
#endif
for (T j = 1; j <= i; j *= 2) {
#ifdef HAVE_PROTOBUF
pb_t.add_b(j * 3);
#endif
t.b.push_back(j * 3);
}
check_self(t);
#ifdef HAVE_PROTOBUF
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestSfixed32, MyTestSfixed32> {
bool operator()(const test_struct_pb::MyTestSfixed32& t,
const MyTestSfixed32& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing sfixed32") {
using T = int32_t;
T max_val = 4;
max_val *= INT16_MAX;
for (T i = 1; i < max_val; i *= -2) {
test_struct_pb::MyTestSfixed32 t{.a = i};
#ifdef HAVE_PROTOBUF
MyTestSfixed32 pb_t;
pb_t.set_a(i);
#endif
for (T j = 1; j <= i; j *= -2) {
#ifdef HAVE_PROTOBUF
pb_t.add_b(j * -3);
#endif
t.b.push_back(j * -3);
}
check_self(t);
#ifdef HAVE_PROTOBUF
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestSfixed64, MyTestSfixed64> {
bool operator()(const test_struct_pb::MyTestSfixed64& t,
const MyTestSfixed64& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b();
}
};
#endif
TEST_CASE("testing sfixed64") {
using T = int64_t;
T max_val = 4;
max_val *= INT32_MAX;
for (T i = 1; i < max_val; i *= -2) {
test_struct_pb::MyTestSfixed64 t{.a = i};
#ifdef HAVE_PROTOBUF
MyTestSfixed64 pb_t;
pb_t.set_a(i);
#endif
for (T j = 1; j <= i; j *= -2) {
#ifdef HAVE_PROTOBUF
pb_t.add_b(j * -3);
#endif
t.b.push_back(j * -3);
}
check_self(t);
#ifdef HAVE_PROTOBUF
check_with_protobuf(t, pb_t);
#endif
}
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestFieldNumberRandom,
MyTestFieldNumberRandom> {
bool operator()(const test_struct_pb::MyTestFieldNumberRandom& t,
const MyTestFieldNumberRandom& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b() && t.c == pb_t.c() &&
t.f == pb_t.f() && std::abs(t.d - pb_t.d()) < 0.00005f &&
std::abs(t.e - pb_t.e()) < 0.00005f;
}
};
#endif
TEST_CASE("testing random field number") {
test_struct_pb::MyTestFieldNumberRandom t{.a = 666,
.b = 999,
.c = "random",
.d = 3.14,
.e = 3344.123,
.f = {5, 4, 3, 2, 1}};
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestFieldNumberRandom pb_t;
pb_t.set_a(666);
pb_t.set_b(999);
pb_t.set_c("random");
pb_t.set_d(3.14);
pb_t.set_e(3344.123);
pb_t.add_f(5);
pb_t.add_f(4);
pb_t.add_f(3);
pb_t.add_f(2);
pb_t.add_f(1);
check_with_protobuf(t, pb_t);
#endif
}
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::MyTestAll, MyTestAll> {
bool operator()(const test_struct_pb::MyTestAll& t,
const MyTestAll& pb_t) const {
return t.a == pb_t.a() && t.b == pb_t.b() && t.c == pb_t.c() &&
t.d == pb_t.d() && t.e == pb_t.e() && t.f == pb_t.f() &&
t.g == pb_t.g() && t.h == pb_t.h() && t.i == pb_t.i() &&
t.j == pb_t.j() && t.k == pb_t.k() && t.l == pb_t.l() &&
t.m == pb_t.m() && t.n == pb_t.n() && t.o == pb_t.o();
}
};
#endif
TEST_CASE("testing all") {
test_struct_pb::MyTestAll t{};
t.a = -100;
t.b = -200;
t.c = -300;
t.d = -2378;
t.e = 2323;
t.f = 255;
t.g = -890;
t.h = -2369723;
t.i = -23234;
t.j = -239223423;
t.k = 9232983;
t.l = 237982374432;
t.m = true;
t.n = "testing all types";
t.o = "\1\2\3\4\5\t\n666";
check_self(t);
#ifdef HAVE_PROTOBUF
MyTestAll pb_t;
pb_t.set_a(t.a);
pb_t.set_b(t.b);
pb_t.set_c(t.c);
pb_t.set_d(t.d);
pb_t.set_e(t.e);
pb_t.set_f(t.f);
pb_t.set_g(t.g);
pb_t.set_h(t.h);
pb_t.set_i(t.i);
pb_t.set_j(t.j);
pb_t.set_k(t.k);
pb_t.set_l(t.l);
pb_t.set_m(t.m);
pb_t.set_n(t.n);
pb_t.set_o(t.o);
check_with_protobuf(t, pb_t);
#endif
}
TEST_SUITE_END;

View File

@ -1,127 +0,0 @@
syntax = "proto3";
message Test1 {
int32 a = 1;
}
message Test2 {
string b = 2;
}
message Test3 {
Test1 c = 3;
}
message Test4 {
string d = 4;
repeated int32 e = 5;
}
message MyTestDouble {
double a = 1;
double b = 2;
double c = 3;
}
message MyTestFloat {
float a = 1;
float b = 2;
float c = 3;
float d = 4;
float e = 5;
}
message MyTestInt32 {
int32 a = 1;
repeated int32 b = 2;
}
message MyTestInt64 {
int64 a = 1;
repeated int64 b = 2;
}
message MyTestUint32 {
uint32 a = 1;
repeated uint32 b = 2;
}
message MyTestUint64 {
uint64 a = 1;
repeated uint64 b = 2;
}
message MyTestEnum {
enum Color {
Red = 0;
Green = 1;
Blue = 2;
Enum127 = 127;
Enum128 = 128;
}
Color color = 6;
}
message MyTestRepeatedMessage {
repeated MyTestFloat fs = 1;
}
message MyTestSint32 {
sint32 a = 1;
}
message MyTestSint64 {
sint64 b = 2;
}
message MyTestMap {
map<string, int32> e = 3;
}
message MyTestFixed32 {
fixed32 a = 1;
repeated fixed32 b = 2;
}
message MyTestFixed64 {
fixed64 a = 1;
repeated fixed64 b = 2;
}
message MyTestSfixed32 {
sfixed32 a = 1;
repeated sfixed32 b = 2;
}
message MyTestSfixed64 {
sfixed64 a = 1;
repeated sfixed64 b = 2;
}
message MyTestFieldNumberRandom {
int32 a = 6;
sint64 b = 3;
string c = 4;
double d = 5;
float e = 1;
repeated fixed32 f = 128;
}
message MyTestAll {
double a = 1;
float b = 2;
int32 c = 3;
int64 d = 4;
uint32 e = 5;
uint64 f = 6;
sint32 g = 7;
sint64 h = 8;
fixed32 i = 9;
fixed64 j = 10;
sfixed32 k = 11;
sfixed64 l = 12;
bool m = 13;
string n = 14;
bytes o = 15;
}
message SubMessageForOneof {
bool ok = 1;
}
message SampleMessageOneof {
oneof test_oneof {
int32 b = 10;
int32 a = 8;
string name = 4;
SubMessageForOneof sub_message = 9;
}
}

View File

@ -1,46 +0,0 @@
#include "doctest.h"
#include "test_bad_identifiers.struct_pb.h"
using namespace doctest;
TEST_SUITE_BEGIN("test bad identifiers");
TEST_CASE("testing conflicting symbol names") {
protobuf_unittest::TestConflictingSymbolNames message;
message.uint32 = 1;
auto sz = struct_pb::internal::get_needed_size(message);
CHECK(sz == 3);
// Due to different API, the code is useless
message.friend_ = 5;
CHECK(message.friend_ == 5);
message.class_ = 6;
CHECK(message.class_ == 6);
// NO PROTO3
typedef protobuf_unittest::TestConflictingSymbolNamesExtension
ExtensionMessage;
}
TEST_CASE("testing conflicting enum names") {
protobuf_unittest::TestConflictingEnumNames message;
message.conflicting_enum =
protobuf_unittest::TestConflictingEnumNames::while_::and_;
CHECK(1 == static_cast<int>(message.conflicting_enum.value()));
message.conflicting_enum =
protobuf_unittest::TestConflictingEnumNames::while_::XOR;
CHECK(5 == static_cast<int>(message.conflicting_enum.value()));
protobuf_unittest::bool_ conflicting_enum;
conflicting_enum = protobuf_unittest::bool_::NOT_EQ;
CHECK(1 == static_cast<int>(conflicting_enum));
conflicting_enum = protobuf_unittest::bool_::return_;
CHECK(3 == static_cast<int>(conflicting_enum));
}
TEST_CASE("testing conflicting message names") {
protobuf_unittest::NULL_ message;
message.int_ = 123;
CHECK(message.int_ == 123);
}
TEST_CASE("testing conflicting extension") {
protobuf_unittest::TestConflictingSymbolNames message;
// NO_PROTO3
}
TEST_SUITE_END();

View File

@ -1,335 +0,0 @@
/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_PROTOBUF
#include "protobuf_sample.hpp"
#endif
#include "data_def.struct_pb.h"
#include "doctest.h"
#include "helper.hpp"
#include "struct_pb_sample.hpp"
using namespace doctest;
namespace struct_pb_sample {
template <typename T>
bool check_unique_ptr(const T& lhs, const T& rhs) {
if (lhs && rhs) {
return *lhs == *rhs;
}
return !lhs && !rhs;
}
bool operator==(const Vec3& lhs, const Vec3& rhs) {
std::valarray<float> lh({lhs.x, lhs.y, lhs.z});
std::valarray<float> rh({rhs.x, rhs.y, rhs.z});
return (std::abs(lh - rh) < 0.05f).min();
}
bool operator==(const Weapon& lhs, const Weapon& rhs) {
return lhs.name == rhs.name && lhs.damage == rhs.damage;
};
bool operator==(const rect32& lhs, const rect32& rhs) {
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width &&
lhs.height == rhs.height;
}
bool operator==(const rect32s& lhs, const rect32s& rhs) {
return lhs.rect32_list == rhs.rect32_list;
}
bool operator==(const person& lhs, const person& rhs) {
return lhs.id == rhs.id && lhs.name == rhs.name && lhs.age == rhs.age &&
lhs.salary == rhs.salary;
}
bool operator==(const persons& lhs, const persons& rhs) {
return lhs.person_list == rhs.person_list;
}
bool operator==(const Monster& lhs, const Monster& rhs) {
bool ok = check_unique_ptr(lhs.pos, rhs.pos);
if (!ok) {
return false;
}
ok = check_unique_ptr(lhs.equipped, rhs.equipped);
if (!ok) {
return false;
}
return lhs.mana == rhs.mana && lhs.hp == rhs.hp && lhs.name == rhs.name &&
lhs.inventory == rhs.inventory && lhs.color == rhs.color &&
lhs.weapons == rhs.weapons && lhs.path == rhs.path;
};
bool operator==(const Monsters& lhs, const Monsters& rhs) {
return lhs.monsters == rhs.monsters;
}
bool verify(const struct_pb_sample::Weapon& a,
const struct_pb_sample::Weapon& b) {
assert(a.name == b.name);
assert(a.damage == b.damage);
return true;
}
bool verify(const struct_pb_sample::Monster& a,
const struct_pb_sample::Monster& b) {
assert(a.pos && b.pos);
assert(*a.pos == *b.pos);
assert(a.mana == b.mana);
assert(a.hp == b.hp);
assert(a.name == b.name);
assert(a.inventory == b.inventory);
assert(a.color == b.color);
assert(a.weapons.size() == b.weapons.size());
for (int i = 0; i < a.weapons.size(); ++i) {
auto ok = verify(a.weapons[i], b.weapons[i]);
if (!ok) {
return ok;
}
}
assert(a.weapons == b.weapons);
assert(a.equipped && b.equipped);
assert(*a.equipped == *b.equipped);
assert(a.path == b.path);
return true;
}
bool verify(const struct_pb_sample::Monsters& a,
const struct_pb_sample::Monsters& b) {
assert(a.monsters.size() == b.monsters.size());
for (int i = 0; i < a.monsters.size(); ++i) {
auto ok = verify(a.monsters[i], b.monsters[i]);
if (!ok) {
return ok;
}
}
return true;
}
template <typename T>
T copy(const T& t) {
return t;
}
template <>
struct_pb_sample::Monster copy(const struct_pb_sample::Monster& t) {
struct_pb_sample::Monster m;
m.pos = std::make_unique<struct_pb_sample::Vec3>();
*m.pos = *t.pos;
m.mana = t.mana;
m.hp = t.hp;
m.name = t.name;
m.inventory = t.inventory;
m.color = t.color;
m.weapons = t.weapons;
m.equipped = std::make_unique<struct_pb_sample::Weapon>();
*m.equipped = *t.equipped;
m.path = t.path;
return m;
}
template <>
struct_pb_sample::Monsters copy(const struct_pb_sample::Monsters& t) {
struct_pb_sample::Monsters m;
m.monsters.reserve(t.monsters.size());
for (const auto& e : t.monsters) {
m.monsters.push_back(std::move(copy(e)));
}
return m;
}
} // namespace struct_pb_sample
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<struct_pb_sample::rect32s, mygame::rect32s> {
bool operator()(const struct_pb_sample::rect32s& t,
const mygame::rect32s& pb_t) const {
return t.rect32_list == pb_t.rect32_list();
}
};
template <>
struct PB_equal<struct_pb_sample::rect32, mygame::rect32> {
bool operator()(const struct_pb_sample::rect32& lhs,
const mygame::rect32& rhs) const {
return lhs.x == rhs.x() && lhs.y == rhs.y() && lhs.width == rhs.width() &&
lhs.height == rhs.height();
}
};
template <>
struct PB_equal<struct_pb_sample::Vec3, mygame::Vec3> {
bool operator()(const struct_pb_sample::Vec3& lhs,
const mygame::Vec3& rhs) const {
return lhs.x == rhs.x() && lhs.y == rhs.y() && lhs.z == rhs.z();
}
};
template <>
struct PB_equal<struct_pb_sample::person, mygame::person> {
bool operator()(const struct_pb_sample::person& lhs,
const mygame::person& rhs) const {
return lhs.id == rhs.id() && lhs.name == rhs.name() &&
lhs.age == rhs.age() &&
std::abs(lhs.salary - rhs.salary()) < 0.0005f;
}
};
template <>
struct PB_equal<struct_pb_sample::persons, mygame::persons> {
bool operator()(const struct_pb_sample::persons& lhs,
const mygame::persons& rhs) const {
return lhs.person_list == rhs.person_list();
}
};
template <>
struct PB_equal<struct_pb_sample::Weapon, mygame::Weapon> {
bool operator()(const struct_pb_sample::Weapon& lhs,
const mygame::Weapon& rhs) const {
return lhs.name == rhs.name() && lhs.damage == rhs.damage();
}
};
bool operator==(const struct_pb_sample::Monster::Color& a,
const mygame::Monster::Color& b) {
return int(a) == int(b);
}
template <>
struct PB_equal<struct_pb_sample::Monster, mygame::Monster> {
bool operator()(const struct_pb_sample::Monster& lhs,
const mygame::Monster& rhs) const {
auto ok = (lhs.name == rhs.name() && lhs.mana == rhs.mana() &&
lhs.hp == rhs.hp() && lhs.inventory == rhs.inventory() &&
lhs.color == rhs.color() && lhs.weapons == rhs.weapons() &&
lhs.path == rhs.path());
if (!ok) {
return false;
}
if (lhs.pos && rhs.has_pos()) {
ok =
PB_equal<struct_pb_sample::Vec3, mygame::Vec3>()(*lhs.pos, rhs.pos());
if (!ok) {
return false;
}
}
else if (lhs.pos || rhs.has_pos()) {
return false;
}
if (lhs.equipped && rhs.has_equipped()) {
ok = PB_equal<struct_pb_sample::Weapon, mygame::Weapon>()(*lhs.equipped,
rhs.equipped());
if (!ok) {
return false;
}
}
else if (lhs.equipped || rhs.has_equipped()) {
return false;
}
return true;
}
};
template <>
struct PB_equal<struct_pb_sample::Monsters, mygame::Monsters> {
bool operator()(const struct_pb_sample::Monsters& lhs,
const mygame::Monsters& rhs) const {
return lhs.monsters == rhs.monsters();
}
};
template <typename T, typename U>
void check_repeated(const std::vector<T>& a,
const google::protobuf::RepeatedPtrField<U>& b) {
bool ret = (a == b);
REQUIRE(ret);
}
#endif
TEST_SUITE_BEGIN("test pb benchmark struct");
TEST_CASE("testing rect") {
auto my_rects = struct_pb_sample::create_rects(OBJECT_COUNT);
#ifdef HAVE_PROTOBUF
auto pb_rects = protobuf_sample::create_rects(OBJECT_COUNT);
#endif
for (int i = 0; i < OBJECT_COUNT; ++i) {
auto my_rect = my_rects.rect32_list[i];
check_self(my_rect);
#ifdef HAVE_PROTOBUF
auto pb_rect = *(pb_rects.rect32_list().begin() + i);
REQUIRE(my_rect.x == pb_rect.x());
REQUIRE(my_rect.y == pb_rect.y());
REQUIRE(my_rect.width == pb_rect.width());
REQUIRE(my_rect.height == pb_rect.height());
check_with_protobuf(my_rect, pb_rect);
#endif
}
check_self(my_rects);
#ifdef HAVE_PROTOBUF
check_with_protobuf(my_rects, pb_rects);
#endif
}
TEST_CASE("testing person") {
auto my_persons = struct_pb_sample::create_persons(OBJECT_COUNT);
#ifdef HAVE_PROTOBUF
auto pb_persons = protobuf_sample::create_persons(OBJECT_COUNT);
#endif
for (int i = 0; i < OBJECT_COUNT; ++i) {
auto my_person = my_persons.person_list[i];
check_self(my_person);
#ifdef HAVE_PROTOBUF
auto pb_person = *(pb_persons.person_list().begin() + i);
REQUIRE(my_person.id == pb_person.id());
REQUIRE(my_person.name == pb_person.name());
REQUIRE(my_person.age == pb_person.age());
REQUIRE(my_person.salary == pb_person.salary());
check_with_protobuf(my_person, pb_person);
#endif
}
check_self(my_persons);
#ifdef HAVE_PROTOBUF
check_with_protobuf(my_persons, pb_persons);
#endif
}
TEST_CASE("testing monsters") {
using namespace struct_pb_sample;
auto my_ms = struct_pb_sample::create_monsters(OBJECT_COUNT);
#ifdef HAVE_PROTOBUF
auto pb_ms = protobuf_sample::create_monsters(OBJECT_COUNT);
#endif
for (int i = 0; i < OBJECT_COUNT; ++i) {
auto& my_m = my_ms.monsters[i];
check_self(my_m);
#ifdef HAVE_PROTOBUF
auto pb_m = *(pb_ms.monsters().begin() + i);
REQUIRE(my_m.pos);
REQUIRE(my_m.mana == pb_m.mana());
REQUIRE(my_m.hp == pb_m.hp());
REQUIRE(my_m.name == pb_m.name());
REQUIRE(my_m.inventory == pb_m.inventory());
REQUIRE(my_m.color == pb_m.color());
check_repeated(my_m.weapons, pb_m.weapons());
check_repeated(my_m.path, pb_m.path());
check_with_protobuf(my_m, pb_m);
#endif
std::string my_buf = struct_pb::serialize<std::string>(my_m);
struct_pb_sample::Monster d_t{};
auto ok = struct_pb::deserialize_to(d_t, my_buf);
REQUIRE(ok);
CHECK(struct_pb_sample::verify(d_t, my_m));
REQUIRE(d_t.pos);
REQUIRE(my_m.pos);
CHECK(*d_t.pos == *my_m.pos);
CHECK(d_t.mana == my_m.mana);
CHECK(d_t.hp == my_m.hp);
CHECK(d_t.name == my_m.name);
CHECK(d_t.inventory == my_m.inventory);
CHECK(d_t.color == my_m.color);
REQUIRE(d_t.equipped);
REQUIRE(my_m.equipped);
CHECK(*d_t.equipped == *my_m.equipped);
CHECK(d_t.path == my_m.path);
}
check_self(my_ms);
#ifdef HAVE_PROTOBUF
check_with_protobuf(my_ms, pb_ms);
#endif
}
TEST_SUITE_END;

View File

@ -1,108 +0,0 @@
#include "doctest.h"
#include "helper.hpp"
#ifdef HAVE_PROTOBUF
#include "test_pb.pb.h"
#endif
using namespace doctest;
#ifdef HAVE_PROTOBUF
template <>
struct PB_equal<test_struct_pb::SubMessageForOneof, SubMessageForOneof> {
bool operator()(const test_struct_pb::SubMessageForOneof& t,
const SubMessageForOneof& pb_t) const {
return t.ok == pb_t.ok();
}
};
template <>
struct PB_equal<test_struct_pb::SampleMessageOneof, SampleMessageOneof> {
bool operator()(const test_struct_pb::SampleMessageOneof& t,
const SampleMessageOneof& pb_t) const {
if (int(t.test_oneof_case()) != int(pb_t.test_oneof_case())) {
return false;
}
switch (pb_t.test_oneof_case()) {
case SampleMessageOneof::kB: {
return t.b() == pb_t.b();
} break;
case SampleMessageOneof::kA: {
return t.a() == pb_t.a();
} break;
case SampleMessageOneof::kName: {
return t.name() == pb_t.name();
} break;
case SampleMessageOneof::kSubMessage: {
return PB_equal<test_struct_pb::SubMessageForOneof,
SubMessageForOneof>()(*t.sub_message(),
pb_t.sub_message());
} break;
case SampleMessageOneof::TEST_ONEOF_NOT_SET:
break;
}
return true;
}
};
#endif
namespace std {
template <>
struct equal_to<test_struct_pb::SampleMessageOneof> {
bool operator()(const test_struct_pb::SampleMessageOneof& lhs,
const test_struct_pb::SampleMessageOneof& rhs) const {
if (lhs.test_oneof_case() != rhs.test_oneof_case()) {
return false;
}
if (lhs.has_sub_message() && rhs.has_sub_message()) {
return equal_to<test_struct_pb::SubMessageForOneof>()(*lhs.sub_message(),
*rhs.sub_message());
}
return lhs.test_oneof == rhs.test_oneof;
}
};
} // namespace std
TEST_CASE("testing oneof, b") {
test_struct_pb::SampleMessageOneof t;
int b = 13298;
t.set_b(b);
check_self(t);
#ifdef HAVE_PROTOBUF
SampleMessageOneof pb_t;
pb_t.set_b(b);
check_with_protobuf(t, pb_t);
#endif
}
TEST_CASE("testing oneof, a") {
test_struct_pb::SampleMessageOneof t;
int a = 66613298;
t.set_a(a);
check_self(t);
#ifdef HAVE_PROTOBUF
SampleMessageOneof pb_t;
pb_t.set_a(a);
check_with_protobuf(t, pb_t);
#endif
}
TEST_CASE("testing oneof, name") {
std::string name = "oneof, name";
test_struct_pb::SampleMessageOneof t;
t.set_name(name);
check_self(t);
#ifdef HAVE_PROTOBUF
SampleMessageOneof pb_t;
pb_t.set_name(name);
check_with_protobuf(t, pb_t);
#endif
}
TEST_CASE("testing oneof, submessage") {
test_struct_pb::SampleMessageOneof t;
auto sub = new test_struct_pb::SubMessageForOneof{true};
t.set_allocated_sub_message(sub);
check_self(t);
#ifdef HAVE_PROTOBUF
SampleMessageOneof pb_t;
auto m = new SubMessageForOneof();
m->set_ok(true);
pb_t.set_allocated_sub_message(m);
check_with_protobuf(t, pb_t);
#endif
}

View File

@ -0,0 +1,688 @@
#pragma once
#include <ylt/struct_pb.hpp>
#if defined(STRUCT_PB_WITH_PROTO)
#include "data_def.pb.h"
#include "unittest_proto3.pb.h"
#endif
#define PUBLIC(T) : public iguana::base_impl<T>
// define the struct as msg in proto
namespace stpb {
enum class Enum {
ZERO = 0,
FOO = 1,
BAR = 2,
BAZ = 123456,
NEG = -1, // Intentionally negative.
};
struct BaseTypeMsg PUBLIC(BaseTypeMsg) {
BaseTypeMsg() = default;
BaseTypeMsg(int32_t a, int64_t b, uint32_t c, uint64_t d, float e, double f,
bool g, std::string h, Enum i)
: optional_int32(a),
optional_int64(b),
optional_uint32(c),
optional_uint64(d),
optional_float(e),
optional_double(f),
optional_bool(g),
optional_string(std::move(h)),
optional_enum(i) {}
int32_t optional_int32;
int64_t optional_int64;
uint32_t optional_uint32;
uint64_t optional_uint64;
float optional_float;
double optional_double;
bool optional_bool;
std::string optional_string;
Enum optional_enum;
bool operator==(const BaseTypeMsg& other) const {
return optional_int32 == other.optional_int32 &&
optional_int64 == other.optional_int64 &&
optional_uint32 == other.optional_uint32 &&
optional_uint64 == other.optional_uint64 &&
optional_float == other.optional_float &&
optional_double == other.optional_double &&
optional_bool == other.optional_bool &&
optional_string == other.optional_string &&
optional_enum == other.optional_enum;
}
};
REFLECTION(BaseTypeMsg, optional_int32, optional_int64, optional_uint32,
optional_uint64, optional_float, optional_double, optional_bool,
optional_string, optional_enum);
struct IguanaTypeMsg PUBLIC(IguanaTypeMsg) {
IguanaTypeMsg() = default;
IguanaTypeMsg(iguana::sint32_t a, iguana::sint64_t b, iguana::fixed32_t c,
iguana::fixed64_t d = {}, iguana::sfixed32_t e = {},
iguana::sfixed64_t f = {})
: optional_sint32(a),
optional_sint64(b),
optional_fixed32(c),
optional_fixed64(d),
optional_sfixed32(e),
optional_sfixed64(f) {}
iguana::sint32_t optional_sint32;
iguana::sint64_t optional_sint64;
iguana::fixed32_t optional_fixed32;
iguana::fixed64_t optional_fixed64;
iguana::sfixed32_t optional_sfixed32;
iguana::sfixed64_t optional_sfixed64;
bool operator==(const IguanaTypeMsg& other) const {
return optional_sint32 == other.optional_sint32 &&
optional_sint64 == other.optional_sint64 &&
optional_fixed32 == other.optional_fixed32 &&
optional_fixed64 == other.optional_fixed64 &&
optional_sfixed32 == other.optional_sfixed32 &&
optional_sfixed64 == other.optional_sfixed64;
}
};
REFLECTION(IguanaTypeMsg, optional_sint32, optional_sint64, optional_fixed32,
optional_fixed64, optional_sfixed32, optional_sfixed64);
struct RepeatBaseTypeMsg PUBLIC(RepeatBaseTypeMsg) {
RepeatBaseTypeMsg() = default;
RepeatBaseTypeMsg(std::vector<uint32_t> a, std::vector<uint64_t> b,
std::vector<int32_t> c, std::vector<int64_t> d,
std::vector<float> e, std::vector<double> f,
std::vector<std::string> g, std::vector<Enum> h)
: repeated_uint32(std::move(a)),
repeated_uint64(std::move(b)),
repeated_int32(std::move(c)),
repeated_int64(std::move(d)),
repeated_float(std::move(e)),
repeated_double(std::move(f)),
repeated_string(std::move(g)),
repeated_enum(std::move(h)) {}
std::vector<uint32_t> repeated_uint32;
std::vector<uint64_t> repeated_uint64;
std::vector<int32_t> repeated_int32;
std::vector<int64_t> repeated_int64;
std::vector<float> repeated_float;
std::vector<double> repeated_double;
std::vector<std::string> repeated_string;
std::vector<Enum> repeated_enum;
};
REFLECTION(RepeatBaseTypeMsg, repeated_uint32, repeated_uint64, repeated_int32,
repeated_int64, repeated_float, repeated_double, repeated_string,
repeated_enum);
struct RepeatIguanaTypeMsg PUBLIC(RepeatIguanaTypeMsg) {
RepeatIguanaTypeMsg() = default;
RepeatIguanaTypeMsg(std::vector<iguana::sfixed32_t> a,
std::vector<iguana::sfixed64_t> b,
std::vector<iguana::fixed32_t> c,
std::vector<iguana::fixed64_t> d,
std::vector<iguana::sfixed32_t> e,
std::vector<iguana::sfixed64_t> f)
: repeated_sint32(std::move(a)),
repeated_sint64(std::move(b)),
repeated_fixed32(std::move(c)),
repeated_fixed64(std::move(d)),
repeated_sfixed32(std::move(e)),
repeated_sfixed64(std::move(f)) {}
std::vector<iguana::sfixed32_t> repeated_sint32;
std::vector<iguana::sfixed64_t> repeated_sint64;
std::vector<iguana::fixed32_t> repeated_fixed32;
std::vector<iguana::fixed64_t> repeated_fixed64;
std::vector<iguana::sfixed32_t> repeated_sfixed32;
std::vector<iguana::sfixed64_t> repeated_sfixed64;
};
REFLECTION(RepeatIguanaTypeMsg, repeated_sint32, repeated_sint64,
repeated_fixed32, repeated_fixed64, repeated_sfixed32,
repeated_sfixed64);
struct NestedMsg PUBLIC(NestedMsg) {
NestedMsg() = default;
NestedMsg(BaseTypeMsg a, std::vector<BaseTypeMsg> b, IguanaTypeMsg c,
std::vector<IguanaTypeMsg> d, std::vector<RepeatBaseTypeMsg> e)
: base_msg(std::move(a)),
repeat_base_msg(std::move(b)),
iguana_type_msg(std::move(c)),
repeat_iguna_msg(std::move(d)),
repeat_repeat_base_msg(std::move(e)) {}
BaseTypeMsg base_msg;
std::vector<BaseTypeMsg> repeat_base_msg;
IguanaTypeMsg iguana_type_msg;
std::vector<IguanaTypeMsg> repeat_iguna_msg;
std::vector<RepeatBaseTypeMsg> repeat_repeat_base_msg;
};
REFLECTION(NestedMsg, base_msg, repeat_base_msg, iguana_type_msg,
repeat_iguna_msg, repeat_repeat_base_msg);
struct MapMsg PUBLIC(MapMsg) {
MapMsg() = default;
MapMsg(std::unordered_map<iguana::sfixed64_t, std::string> a,
std::unordered_map<std::string, IguanaTypeMsg> b,
std::map<int, RepeatBaseTypeMsg> c)
: sfix64_str_map(std::move(a)),
str_iguana_type_msg_map(std::move(b)),
int_repeat_base_msg_map(std::move(c)) {}
std::unordered_map<iguana::sfixed64_t, std::string> sfix64_str_map{};
std::unordered_map<std::string, IguanaTypeMsg> str_iguana_type_msg_map{};
std::map<int, RepeatBaseTypeMsg> int_repeat_base_msg_map{};
};
REFLECTION(MapMsg, sfix64_str_map, str_iguana_type_msg_map,
int_repeat_base_msg_map);
struct BaseOneofMsg PUBLIC(BaseOneofMsg) {
BaseOneofMsg() = default;
BaseOneofMsg(int32_t a, std::variant<double, std::string, BaseTypeMsg> b,
double c)
: optional_int32(a), one_of(std::move(b)), optional_double(c) {}
int32_t optional_int32;
std::variant<double, std::string, BaseTypeMsg> one_of;
double optional_double;
};
REFLECTION(BaseOneofMsg, optional_int32, one_of, optional_double);
struct NestOneofMsg PUBLIC(NestOneofMsg) {
NestOneofMsg() = default;
NestOneofMsg(std::variant<std::string, BaseOneofMsg> a)
: nest_one_of_msg(std::move(a)) {}
std::variant<std::string, BaseOneofMsg> nest_one_of_msg;
};
REFLECTION(NestOneofMsg, nest_one_of_msg);
struct simple_t PUBLIC(simple_t) {
simple_t() = default;
simple_t(int32_t x, int32_t y, int64_t z, int64_t w, std::string s)
: a(x), b(y), c(z), d(w), str(std::move(s)) {}
int32_t a;
int32_t b;
int64_t c;
int64_t d;
std::string str;
};
REFLECTION(simple_t, a, b, c, d, str);
struct simple_t1 PUBLIC(simple_t1) {
simple_t1() = default;
simple_t1(int32_t x, int32_t y, int64_t z, int64_t w, std::string_view s)
: a(x), b(y), c(z), d(w), str(s) {}
int32_t a;
int32_t b;
int64_t c;
int64_t d;
std::string_view str;
};
REFLECTION(simple_t1, a, b, c, d, str);
enum Color : uint8_t { Red, Green, Blue };
struct simple_t2 PUBLIC(simple_t2) {
simple_t2() = default;
simple_t2(int16_t x, uint8_t y, Color z, int64_t w, std::string_view s = "")
: a(x), b(y), c(z), d(w), str(s) {}
int16_t a;
uint8_t b;
Color c;
int64_t d;
std::string_view str;
};
REFLECTION(simple_t2, a, b, c, d, str);
struct person {
person() = default;
person(int32_t a, std::string b, int c, double d)
: id(a), name(std::move(b)), age(c), salary(d) {}
int32_t id;
std::string name;
int age;
double salary;
};
REFLECTION(person, id, name, age, salary);
struct rect PUBLIC(rect) {
rect() = default;
rect(int32_t a, int32_t b, int32_t c, int32_t d)
: x(a), y(b), width(c), height(d) {}
int32_t x = 1;
int32_t y = 0;
int32_t width = 11;
int32_t height = 1;
};
REFLECTION(rect, x, y, width, height);
struct Vec3 PUBLIC(Vec3) {
Vec3() = default;
Vec3(float a, float b, float c) : x(a), y(b), z(c) {}
float x;
float y;
float z;
REFLECTION(Vec3, x, y, z);
};
struct Weapon PUBLIC(Weapon) {
Weapon() = default;
Weapon(std::string a, int32_t b) : name(std::move(a)), damage(b) {}
std::string name;
int32_t damage;
};
REFLECTION(Weapon, name, damage);
struct Monster PUBLIC(Monster) {
Monster() = default;
Monster(Vec3 a, int32_t b, int32_t c, std::string d, std::string e, int32_t f,
std::vector<Weapon> g, Weapon h, std::vector<Vec3> i)
: pos(a),
mana(b),
hp(c),
name(std::move(d)),
inventory(std::move(e)),
color(f),
weapons(std::move(g)),
equipped(std::move(h)),
path(std::move(i)) {}
Vec3 pos;
int32_t mana;
int32_t hp;
std::string name;
std::string inventory;
int32_t color;
std::vector<Weapon> weapons;
Weapon equipped;
std::vector<Vec3> path;
};
REFLECTION(Monster, pos, mana, hp, name, inventory, color, weapons, equipped,
path);
struct bench_int32 PUBLIC(bench_int32) {
bench_int32() = default;
bench_int32(int32_t x, int32_t y, int32_t z, int32_t u)
: a(x), b(y), c(z), d(u) {}
int32_t a;
int32_t b;
int32_t c;
int32_t d;
};
REFLECTION(bench_int32, a, b, c, d);
} // namespace stpb
inline auto create_person() {
stpb::person p{432798, std::string(1024, 'A'), 24, 65536.42};
return p;
}
inline stpb::Monster create_sp_monster() {
stpb::Monster m = {
{1, 2, 3},
16,
24,
"it is a test",
"\1\2\3\4",
stpb::Color::Red,
{{"gun", 42}, {"shotgun", 56}},
{"air craft", 67},
{{7, 8, 9}, {71, 81, 91}},
};
return m;
}
#if defined(STRUCT_PB_WITH_PROTO)
namespace protobuf_sample {
inline mygame::person create_person() {
mygame::person p;
p.set_id(432798);
p.set_name(std::string(1024, 'A'));
p.set_age(24);
p.set_salary(65536.42);
return p;
}
inline mygame::Monster create_monster() {
mygame::Monster m;
auto vec = new mygame::Vec3;
vec->set_x(1);
vec->set_y(2);
vec->set_z(3);
m.set_allocated_pos(vec);
m.set_mana(16);
m.set_hp(24);
m.set_name("it is a test");
m.set_inventory("\1\2\3\4");
m.set_color(::mygame::Monster_Color::Monster_Color_Red);
auto w1 = m.add_weapons();
w1->set_name("gun");
w1->set_damage(42);
auto w2 = m.add_weapons();
w2->set_name("shotgun");
w2->set_damage(56);
auto w3 = new mygame::Weapon;
w3->set_name("air craft");
w3->set_damage(67);
m.set_allocated_equipped(w3);
auto p1 = m.add_path();
p1->set_x(7);
p1->set_y(8);
p1->set_z(9);
auto p2 = m.add_path();
p2->set_x(71);
p2->set_y(81);
p2->set_z(91);
return m;
}
} // namespace protobuf_sample
void SetBaseTypeMsg(const stpb::BaseTypeMsg& st, pb::BaseTypeMsg& msg) {
msg.set_optional_int32(st.optional_int32);
msg.set_optional_int64(st.optional_int64);
msg.set_optional_uint32(st.optional_uint32);
msg.set_optional_uint64(st.optional_uint64);
msg.set_optional_float(st.optional_float);
msg.set_optional_double(st.optional_double);
msg.set_optional_bool(st.optional_bool);
msg.set_optional_string(st.optional_string);
msg.set_optional_enum(static_cast<pb::Enum>(st.optional_enum));
}
void CheckBaseTypeMsg(const stpb::BaseTypeMsg& st, const pb::BaseTypeMsg& msg) {
CHECK(st.optional_int32 == msg.optional_int32());
CHECK(st.optional_int64 == msg.optional_int64());
CHECK(st.optional_uint32 == msg.optional_uint32());
CHECK(st.optional_uint64 == msg.optional_uint64());
CHECK(st.optional_float == msg.optional_float());
CHECK(st.optional_double == msg.optional_double());
CHECK(st.optional_bool == msg.optional_bool());
CHECK(st.optional_string == msg.optional_string());
CHECK(static_cast<int32_t>(st.optional_enum) ==
static_cast<int32_t>(msg.optional_enum()));
}
void SetIguanaTypeMsg(const stpb::IguanaTypeMsg& st, pb::IguanaTypeMsg& msg) {
msg.set_optional_sint32(st.optional_sint32.val);
msg.set_optional_sint64(st.optional_sint64.val);
msg.set_optional_fixed32(st.optional_fixed32.val);
msg.set_optional_fixed64(st.optional_fixed64.val);
msg.set_optional_sfixed32(st.optional_sfixed32.val);
msg.set_optional_sfixed64(st.optional_sfixed64.val);
}
void CheckIguanaTypeMsg(const stpb::IguanaTypeMsg& st,
const pb::IguanaTypeMsg& msg) {
CHECK(st.optional_sint32.val == msg.optional_sint32());
CHECK(st.optional_sint64.val == msg.optional_sint64());
CHECK(st.optional_fixed32.val == msg.optional_fixed32());
CHECK(st.optional_fixed64.val == msg.optional_fixed64());
CHECK(st.optional_sfixed32.val == msg.optional_sfixed32());
CHECK(st.optional_sfixed64.val == msg.optional_sfixed64());
}
void SetRepeatBaseTypeMsg(const stpb::RepeatBaseTypeMsg& st,
pb::RepeatBaseTypeMsg& msg) {
for (auto v : st.repeated_uint32) {
msg.add_repeated_uint32(v);
}
for (auto v : st.repeated_uint64) {
msg.add_repeated_uint64(v);
}
for (auto v : st.repeated_int32) {
msg.add_repeated_int32(v);
}
for (auto v : st.repeated_int64) {
msg.add_repeated_int64(v);
}
for (auto v : st.repeated_float) {
msg.add_repeated_float(v);
}
for (auto v : st.repeated_double) {
msg.add_repeated_double(v);
}
for (auto v : st.repeated_string) {
msg.add_repeated_string(v);
}
for (auto v : st.repeated_enum) {
msg.add_repeated_enum(static_cast<pb::Enum>(v));
}
}
void CheckRepeatBaseTypeMsg(const stpb::RepeatBaseTypeMsg& st,
const pb::RepeatBaseTypeMsg& msg) {
for (size_t i = 0; i < st.repeated_uint32.size(); ++i) {
CHECK(st.repeated_uint32[i] == msg.repeated_uint32(i));
}
for (size_t i = 0; i < st.repeated_uint64.size(); ++i) {
CHECK(st.repeated_uint64[i] == msg.repeated_uint64(i));
}
for (size_t i = 0; i < st.repeated_int32.size(); ++i) {
CHECK(st.repeated_int32[i] == msg.repeated_int32(i));
}
for (size_t i = 0; i < st.repeated_int64.size(); ++i) {
CHECK(st.repeated_int64[i] == msg.repeated_int64(i));
}
for (size_t i = 0; i < st.repeated_float.size(); ++i) {
CHECK(st.repeated_float[i] == msg.repeated_float(i));
}
for (size_t i = 0; i < st.repeated_double.size(); ++i) {
CHECK(st.repeated_double[i] == msg.repeated_double(i));
}
for (size_t i = 0; i < st.repeated_string.size(); ++i) {
CHECK(st.repeated_string[i] == msg.repeated_string(i));
}
for (size_t i = 0; i < st.repeated_enum.size(); ++i) {
CHECK(static_cast<int>(st.repeated_enum[i]) ==
static_cast<int>(msg.repeated_enum(i)));
}
}
void SetRepeatIguanaTypeMsg(const stpb::RepeatIguanaTypeMsg& st,
pb::RepeatIguanaTypeMsg& msg) {
for (auto v : st.repeated_sint32) {
msg.add_repeated_sint32(v.val);
}
for (auto v : st.repeated_sint64) {
msg.add_repeated_sint64(v.val);
}
for (auto v : st.repeated_fixed32) {
msg.add_repeated_fixed32(v.val);
}
for (auto v : st.repeated_fixed64) {
msg.add_repeated_fixed64(v.val);
}
for (auto v : st.repeated_sfixed32) {
msg.add_repeated_sfixed32(v.val);
}
for (auto v : st.repeated_sfixed64) {
msg.add_repeated_sfixed64(v.val);
}
}
void CheckRepeatIguanaTypeMsg(const stpb::RepeatIguanaTypeMsg& st,
const pb::RepeatIguanaTypeMsg& msg) {
for (size_t i = 0; i < st.repeated_sint32.size(); ++i) {
CHECK(st.repeated_sint32[i].val == msg.repeated_sint32(i));
}
for (size_t i = 0; i < st.repeated_sint64.size(); ++i) {
CHECK(st.repeated_sint64[i].val == msg.repeated_sint64(i));
}
for (size_t i = 0; i < st.repeated_fixed32.size(); ++i) {
CHECK(st.repeated_fixed32[i].val == msg.repeated_fixed32(i));
}
for (size_t i = 0; i < st.repeated_fixed64.size(); ++i) {
CHECK(st.repeated_fixed64[i].val == msg.repeated_fixed64(i));
}
for (size_t i = 0; i < st.repeated_sfixed32.size(); ++i) {
CHECK(st.repeated_sfixed32[i].val == msg.repeated_sfixed32(i));
}
for (size_t i = 0; i < st.repeated_sfixed64.size(); ++i) {
CHECK(st.repeated_sfixed64[i].val == msg.repeated_sfixed64(i));
}
}
void SetNestedMsg(const stpb::NestedMsg& st, pb::NestedMsg& msg) {
SetBaseTypeMsg(st.base_msg, *msg.mutable_base_msg());
for (const auto& base_msg : st.repeat_base_msg) {
auto* base_msg_ptr = msg.add_repeat_base_msg();
SetBaseTypeMsg(base_msg, *base_msg_ptr);
}
SetIguanaTypeMsg(st.iguana_type_msg, *msg.mutable_iguana_type_msg());
for (const auto& iguana_type_msg : st.repeat_iguna_msg) {
auto* iguana_type_msg_ptr = msg.add_repeat_iguna_msg();
SetIguanaTypeMsg(iguana_type_msg, *iguana_type_msg_ptr);
}
for (const auto& repeat_base_msg : st.repeat_repeat_base_msg) {
auto* repeat_base_msg_ptr = msg.add_repeat_repeat_base_msg();
SetRepeatBaseTypeMsg(repeat_base_msg, *repeat_base_msg_ptr);
}
}
void CheckNestedMsg(const stpb::NestedMsg& st, const pb::NestedMsg& msg) {
CheckBaseTypeMsg(st.base_msg, msg.base_msg());
CHECK(st.repeat_base_msg.size() == msg.repeat_base_msg_size());
for (size_t i = 0; i < st.repeat_base_msg.size(); ++i) {
CheckBaseTypeMsg(st.repeat_base_msg[i], msg.repeat_base_msg(i));
}
CheckIguanaTypeMsg(st.iguana_type_msg, msg.iguana_type_msg());
CHECK(st.repeat_iguna_msg.size() == msg.repeat_iguna_msg_size());
for (size_t i = 0; i < st.repeat_iguna_msg.size(); ++i) {
CheckIguanaTypeMsg(st.repeat_iguna_msg[i], msg.repeat_iguna_msg(i));
}
CHECK(st.repeat_repeat_base_msg.size() == msg.repeat_repeat_base_msg_size());
for (size_t i = 0; i < st.repeat_repeat_base_msg.size(); ++i) {
CheckRepeatBaseTypeMsg(st.repeat_repeat_base_msg[i],
msg.repeat_repeat_base_msg(i));
}
}
void SetMapMsg(const stpb::MapMsg& st, pb::MapMsg& msg) {
msg.Clear();
for (const auto& pair : st.sfix64_str_map) {
(*msg.mutable_sfix64_str_map())[pair.first.val] = pair.second;
}
for (const auto& pair : st.str_iguana_type_msg_map) {
pb::IguanaTypeMsg* it_msg =
&((*msg.mutable_str_iguana_type_msg_map())[pair.first]);
SetIguanaTypeMsg(pair.second, *it_msg);
}
for (const auto& pair : st.int_repeat_base_msg_map) {
pb::RepeatBaseTypeMsg* rb_msg =
&((*msg.mutable_int_repeat_base_msg_map())[pair.first]);
SetRepeatBaseTypeMsg(pair.second, *rb_msg);
}
}
void CheckMapMsg(const stpb::MapMsg& st, const pb::MapMsg& msg) {
CHECK(msg.sfix64_str_map_size() == st.sfix64_str_map.size());
for (const auto& pair : st.sfix64_str_map) {
auto it = msg.sfix64_str_map().find(pair.first.val);
CHECK(it != msg.sfix64_str_map().end());
CHECK(it->second == pair.second);
}
CHECK(msg.str_iguana_type_msg_map_size() ==
st.str_iguana_type_msg_map.size());
for (const auto& pair : st.str_iguana_type_msg_map) {
auto it = msg.str_iguana_type_msg_map().find(pair.first);
CHECK(it != msg.str_iguana_type_msg_map().end());
CheckIguanaTypeMsg(pair.second, it->second);
}
CHECK(msg.int_repeat_base_msg_map_size() ==
st.int_repeat_base_msg_map.size());
for (const auto& pair : st.int_repeat_base_msg_map) {
auto it = msg.int_repeat_base_msg_map().find(pair.first);
CHECK(it != msg.int_repeat_base_msg_map().end());
CheckRepeatBaseTypeMsg(pair.second, it->second);
}
}
void SetBaseOneofMsg(const stpb::BaseOneofMsg& st, pb::BaseOneofMsg& msg) {
msg.set_optional_int32(st.optional_int32);
msg.set_optional_double(st.optional_double);
std::visit(
[&](auto& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, double>) {
msg.set_one_of_double(value);
}
else if constexpr (std::is_same_v<T, std::string>) {
msg.set_one_of_string(value);
}
else if constexpr (std::is_same_v<T, stpb::BaseTypeMsg>) {
auto* submsg = msg.mutable_one_of_base_type_msg();
SetBaseTypeMsg(value, *submsg);
}
},
st.one_of);
}
void CheckBaseOneofMsg(const stpb::BaseOneofMsg& st,
const pb::BaseOneofMsg& msg) {
CHECK(st.optional_int32 == msg.optional_int32());
CHECK(st.optional_double == msg.optional_double());
std::visit(
[&](auto& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, double>) {
CHECK(value == msg.one_of_double());
}
else if constexpr (std::is_same_v<T, std::string>) {
CHECK(value == msg.one_of_string());
}
else if constexpr (std::is_same_v<T, stpb::BaseTypeMsg>) {
CheckBaseTypeMsg(value, msg.one_of_base_type_msg());
}
},
st.one_of);
}
void SetNestOneofMsg(const stpb::NestOneofMsg& st, pb::NestOneofMsg& msg) {
std::visit(
[&](auto& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, std::string>) {
msg.set_base_one_of_string(value);
}
else if constexpr (std::is_same_v<T, stpb::BaseOneofMsg>) {
auto* submsg = msg.mutable_base_one_of_msg();
SetBaseOneofMsg(value, *submsg);
}
},
st.nest_one_of_msg);
}
void CheckNestOneofMsg(const stpb::NestOneofMsg& st,
const pb::NestOneofMsg& msg) {
std::visit(
[&](auto& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, std::string>) {
CHECK(value == msg.base_one_of_string());
}
else if constexpr (std::is_same_v<T, stpb::BaseOneofMsg>) {
CheckBaseOneofMsg(value, msg.base_one_of_msg());
}
},
st.nest_one_of_msg);
}
#endif
inline void print_hex_str(const std::string& str) {
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (unsigned char c : str) {
oss << std::setw(2) << static_cast<int>(c);
}
std::cout << oss.str() << std::endl;
}

View File

@ -0,0 +1,108 @@
syntax = "proto3";
package pb;
option optimize_for = SPEED;
enum Enum {
ZERO = 0;
FOO = 1;
BAR = 2;
BAZ = 123456;
NEG = -1; // Intentionally negative.
}
message BaseTypeMsg {
int32 optional_int32 = 1;
int64 optional_int64 = 2;
uint32 optional_uint32 = 3;
uint64 optional_uint64 = 4;
float optional_float = 5;
double optional_double = 6;
bool optional_bool = 7;
string optional_string = 8;
Enum optional_enum = 9;
}
message RepeatBaseTypeMsg {
repeated uint32 repeated_uint32 = 1;
repeated uint64 repeated_uint64 = 2;
repeated int32 repeated_int32 = 3;
repeated int64 repeated_int64 = 4;
repeated float repeated_float = 5;
repeated double repeated_double = 6;
repeated string repeated_string = 7;
repeated Enum repeated_enum = 8;
}
message IguanaTypeMsg {
sint32 optional_sint32 = 1;
sint64 optional_sint64 = 2;
fixed32 optional_fixed32 = 3;
fixed64 optional_fixed64 = 4;
sfixed32 optional_sfixed32 = 5;
sfixed64 optional_sfixed64 = 6;
}
message RepeatIguanaTypeMsg {
repeated sfixed32 repeated_sint32 = 1;
repeated sfixed64 repeated_sint64 = 2;
repeated fixed32 repeated_fixed32 = 3;
repeated fixed64 repeated_fixed64 = 4;
repeated sfixed32 repeated_sfixed32 = 5;
repeated sfixed64 repeated_sfixed64 = 6;
}
message NestedMsg {
BaseTypeMsg base_msg = 1;
repeated BaseTypeMsg repeat_base_msg = 2;
IguanaTypeMsg iguana_type_msg = 3;
repeated IguanaTypeMsg repeat_iguna_msg = 4;
repeated RepeatBaseTypeMsg repeat_repeat_base_msg = 5;
}
message MapMsg {
map<sfixed64, string> sfix64_str_map = 1;
map<string, IguanaTypeMsg> str_iguana_type_msg_map = 2;
map<int32, RepeatBaseTypeMsg> int_repeat_base_msg_map = 3;
// Key in map fields cannot be float/double, bytes or message types.
}
message BaseOneofMsg {
int32 optional_int32 = 1;
oneof one_of {
double one_of_double = 2;
string one_of_string = 3;
BaseTypeMsg one_of_base_type_msg = 4;
}
double optional_double = 5;
}
message NestOneofMsg {
oneof nest_one_of_msg {
string base_one_of_string = 1;
BaseOneofMsg base_one_of_msg = 2;
}
}
message Simple {
int32 a = 1;
int32 b = 2;
int64 c = 3;
int64 d = 4;
string str = 5;
}
enum Color {
Red = 0;
Green = 1;
Blue = 2;
}
message Simple2 {
int32 a = 1;
int32 b = 2;
Color c = 3;
int64 d = 4;
string str = 5;
}

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