aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile.am27
-rw-r--r--README.md4
-rw-r--r--appveyor.yml9
-rwxr-xr-xautogen.sh23
-rw-r--r--cmake/README.md10
-rw-r--r--cmake/extract_includes.bat.in7
-rw-r--r--cmake/libprotobuf-lite.cmake6
-rw-r--r--cmake/libprotobuf.cmake19
-rw-r--r--cmake/tests.cmake49
-rw-r--r--configure.ac6
-rw-r--r--src/Makefile.am300
-rw-r--r--src/google/protobuf/compiler/cpp/cpp_message.cc6
-rw-r--r--src/google/protobuf/stubs/bytestream.cc196
-rw-r--r--src/google/protobuf/stubs/bytestream.h348
-rw-r--r--src/google/protobuf/stubs/bytestream_unittest.cc146
-rw-r--r--src/google/protobuf/stubs/casts.h10
-rw-r--r--src/google/protobuf/stubs/common.cc26
-rw-r--r--src/google/protobuf/stubs/common.h307
-rw-r--r--src/google/protobuf/stubs/mathlimits.cc144
-rw-r--r--src/google/protobuf/stubs/mathlimits.h279
-rw-r--r--src/google/protobuf/stubs/mathutil.h149
-rw-r--r--src/google/protobuf/stubs/status.cc135
-rw-r--r--src/google/protobuf/stubs/status.h116
-rw-r--r--src/google/protobuf/stubs/status_macros.h89
-rw-r--r--src/google/protobuf/stubs/status_test.cc131
-rw-r--r--src/google/protobuf/stubs/statusor.cc46
-rw-r--r--src/google/protobuf/stubs/statusor.h259
-rw-r--r--src/google/protobuf/stubs/statusor_test.cc274
-rw-r--r--src/google/protobuf/stubs/stringpiece.cc268
-rw-r--r--src/google/protobuf/stubs/stringpiece.h440
-rw-r--r--src/google/protobuf/stubs/stringpiece_unittest.cc793
-rw-r--r--src/google/protobuf/stubs/strutil.cc768
-rw-r--r--src/google/protobuf/stubs/strutil.h214
-rw-r--r--src/google/protobuf/stubs/strutil_unittest.cc739
-rw-r--r--src/google/protobuf/stubs/time.cc366
-rw-r--r--src/google/protobuf/stubs/time.h75
-rw-r--r--src/google/protobuf/stubs/time_test.cc208
-rw-r--r--src/google/protobuf/text_format.cc4
-rw-r--r--src/google/protobuf/util/field_comparator.cc187
-rw-r--r--src/google/protobuf/util/field_comparator.h259
-rw-r--r--src/google/protobuf/util/field_comparator_test.cc483
-rw-r--r--src/google/protobuf/util/internal/constants.h93
-rw-r--r--src/google/protobuf/util/internal/datapiece.cc285
-rw-r--r--src/google/protobuf/util/internal/datapiece.h212
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter.cc515
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter.h238
-rw-r--r--src/google/protobuf/util/internal/default_value_objectwriter_test.cc139
-rw-r--r--src/google/protobuf/util/internal/error_listener.cc42
-rw-r--r--src/google/protobuf/util/internal/error_listener.h99
-rw-r--r--src/google/protobuf/util/internal/expecting_objectwriter.h238
-rw-r--r--src/google/protobuf/util/internal/field_mask_utility.cc228
-rw-r--r--src/google/protobuf/util/internal/field_mask_utility.h72
-rw-r--r--src/google/protobuf/util/internal/json_escaping.cc403
-rw-r--r--src/google/protobuf/util/internal/json_escaping.h91
-rw-r--r--src/google/protobuf/util/internal/json_objectwriter.cc175
-rw-r--r--src/google/protobuf/util/internal/json_objectwriter.h206
-rw-r--r--src/google/protobuf/util/internal/json_objectwriter_test.cc285
-rw-r--r--src/google/protobuf/util/internal/json_stream_parser.cc740
-rw-r--r--src/google/protobuf/util/internal/json_stream_parser.h256
-rw-r--r--src/google/protobuf/util/internal/json_stream_parser_test.cc702
-rw-r--r--src/google/protobuf/util/internal/location_tracker.h65
-rw-r--r--src/google/protobuf/util/internal/mock_error_listener.h63
-rw-r--r--src/google/protobuf/util/internal/object_location_tracker.h64
-rw-r--r--src/google/protobuf/util/internal/object_source.h79
-rw-r--r--src/google/protobuf/util/internal/object_writer.cc92
-rw-r--r--src/google/protobuf/util/internal/object_writer.h126
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.cc1051
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource.h245
-rw-r--r--src/google/protobuf/util/internal/protostream_objectsource_test.cc824
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.cc1557
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter.h455
-rw-r--r--src/google/protobuf/util/internal/protostream_objectwriter_test.cc1513
-rw-r--r--src/google/protobuf/util/internal/snake2camel_objectwriter.h187
-rw-r--r--src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc311
-rw-r--r--src/google/protobuf/util/internal/structured_objectwriter.h118
-rw-r--r--src/google/protobuf/util/internal/testdata/anys.proto53
-rw-r--r--src/google/protobuf/util/internal/testdata/books.proto171
-rw-r--r--src/google/protobuf/util/internal/testdata/default_value.proto162
-rw-r--r--src/google/protobuf/util/internal/testdata/default_value_test.proto46
-rw-r--r--src/google/protobuf/util/internal/testdata/field_mask.proto71
-rw-r--r--src/google/protobuf/util/internal/testdata/maps.proto57
-rw-r--r--src/google/protobuf/util/internal/testdata/struct.proto45
-rw-r--r--src/google/protobuf/util/internal/testdata/timestamp_duration.proto47
-rw-r--r--src/google/protobuf/util/internal/testdata/wrappers.proto100
-rw-r--r--src/google/protobuf/util/internal/type_info.cc171
-rw-r--r--src/google/protobuf/util/internal/type_info.h87
-rw-r--r--src/google/protobuf/util/internal/type_info_test_helper.cc130
-rw-r--r--src/google/protobuf/util/internal/type_info_test_helper.h98
-rw-r--r--src/google/protobuf/util/internal/utility.cc332
-rw-r--r--src/google/protobuf/util/internal/utility.h187
-rw-r--r--src/google/protobuf/util/json_format_proto3.proto157
-rw-r--r--src/google/protobuf/util/json_util.cc142
-rw-r--r--src/google/protobuf/util/json_util.h136
-rw-r--r--src/google/protobuf/util/json_util_test.cc277
-rw-r--r--src/google/protobuf/util/message_differencer.cc1629
-rw-r--r--src/google/protobuf/util/message_differencer.h817
-rwxr-xr-xsrc/google/protobuf/util/message_differencer_unittest.cc3132
-rw-r--r--src/google/protobuf/util/message_differencer_unittest.proto74
-rw-r--r--src/google/protobuf/util/type_resolver.h75
-rw-r--r--src/google/protobuf/util/type_resolver_util.cc212
-rw-r--r--src/google/protobuf/util/type_resolver_util.h52
-rw-r--r--src/google/protobuf/util/type_resolver_util_test.cc338
103 files changed, 28289 insertions, 207 deletions
diff --git a/.gitignore b/.gitignore
index 52c92dfb..b359dea3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,7 +19,7 @@ m4/lt~obsolete.m4
autom4te.cache
# downloaded files
-gtest
+gmock
# in-tree configure-generated files
Makefile
@@ -47,6 +47,8 @@ any_test.pb.*
map*unittest.pb.*
unittest*.pb.*
cpp_test*.pb.*
+src/google/protobuf/util/**/*.pb.cc
+src/google/protobuf/util/**/*.pb.h
*.pyc
*.egg-info
diff --git a/Makefile.am b/Makefile.am
index 8d7bb563..9d6b9450 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,28 +8,29 @@ AUTOMAKE_OPTIONS = foreign
# the right time.
SUBDIRS = . src
-# Always include gtest in distributions.
+# Always include gmock in distributions.
DIST_SUBDIRS = $(subdirs) src conformance
-# Build gtest before we build protobuf tests. We don't add gtest to SUBDIRS
-# because then "make check" would also build and run all of gtest's own tests,
+# Build gmock before we build protobuf tests. We don't add gmock to SUBDIRS
+# because then "make check" would also build and run all of gmock's own tests,
# which takes a lot of time and is generally not useful to us. Also, we don't
-# want "make install" to recurse into gtest since we don't want to overwrite
-# the installed version of gtest if there is one.
+# want "make install" to recurse into gmock since we don't want to overwrite
+# the installed version of gmock if there is one.
check-local:
- @echo "Making lib/libgtest.a lib/libgtest_main.a in gtest"
- @cd gtest && $(MAKE) $(AM_MAKEFLAGS) lib/libgtest.la lib/libgtest_main.la
+ @echo "Making lib/libgmock.a lib/libgmock_main.a in gmock"
+ @cd gmock && $(MAKE) $(AM_MAKEFLAGS) lib/libgmock.la lib/libgmock_main.la
+ @cd gmock/gtest && $(MAKE) $(AM_MAKEFLAGS) lib/libgtest.la lib/libgtest_main.la
-# We would like to clean gtest when "make clean" is invoked. But we have to
+# We would like to clean gmock when "make clean" is invoked. But we have to
# be careful because clean-local is also invoked during "make distclean", but
-# "make distclean" already recurses into gtest because it's listed among the
-# DIST_SUBDIRS. distclean will delete gtest/Makefile, so if we then try to
+# "make distclean" already recurses into gmock because it's listed among the
+# DIST_SUBDIRS. distclean will delete gmock/Makefile, so if we then try to
# cd to the directory again and "make clean" it will fail. So, check that the
# Makefile exists before recursing.
clean-local:
- @if test -e gtest/Makefile; then \
- echo "Making clean in gtest"; \
- cd gtest && $(MAKE) $(AM_MAKEFLAGS) clean; \
+ @if test -e gmock/Makefile; then \
+ echo "Making clean in gmock"; \
+ cd gmock && $(MAKE) $(AM_MAKEFLAGS) clean; \
fi; \
if test -e conformance/Makefile; then \
echo "Making clean in conformance"; \
diff --git a/README.md b/README.md
index 51377ad7..a974d301 100644
--- a/README.md
+++ b/README.md
@@ -15,12 +15,12 @@ first:
$ ./autogen.sh
-This will download gtest source (which is used for C++ Protocol Buffer
+This will download gmock source (which is used for C++ Protocol Buffer
unit-tests) to the current directory and run automake, autoconf, etc.
to generate the configure script and various template makefiles.
You can skip this step if you are using a release package (which already
-contains gtest and the configure script).
+contains gmock and the configure script).
To build and install the C++ Protocol Buffer runtime and the Protocol
Buffer compiler (protoc) execute the following:
diff --git a/appveyor.yml b/appveyor.yml
index 91862230..3653c041 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -12,9 +12,9 @@ environment:
- BUILD_DLL: ON
install:
- - ps: Start-FileDownload https://googletest.googlecode.com/files/gtest-1.7.0.zip
- - 7z x gtest-1.7.0.zip
- - rename gtest-1.7.0 gtest
+ - ps: Start-FileDownload https://googlemock.googlecode.com/files/gmock-1.7.0.zip
+ - 7z x gmock-1.7.0.zip
+ - rename gmock-1.7.0 gmock
before_build:
- if %platform%==Win32 set generator=Visual Studio 12
@@ -30,4 +30,5 @@ build_script:
- cd %configuration%
- tests.exe
-
+skip_commits:
+ message: /.*\[skip appveyor\].*/
diff --git a/autogen.sh b/autogen.sh
index 08966c63..8160313e 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -15,27 +15,18 @@ __EOF__
exit 1
fi
-# Check that gtest is present. Usually it is already there since the
+# Check that gmock is present. Usually it is already there since the
# directory is set up as an SVN external.
-if test ! -e gtest; then
- echo "Google Test not present. Fetching gtest-1.7.0 from the web..."
- curl -O https://googletest.googlecode.com/files/gtest-1.7.0.zip
- unzip -q gtest-1.7.0.zip
- rm gtest-1.7.0.zip
- mv gtest-1.7.0 gtest
+if test ! -e gmock; then
+ echo "Google Mock not present. Fetching gmock-1.7.0 from the web..."
+ curl -O https://googlemock.googlecode.com/files/gmock-1.7.0.zip
+ unzip -q gmock-1.7.0.zip
+ rm gmock-1.7.0.zip
+ mv gmock-1.7.0 gmock
fi
set -ex
-# Temporary hack: Must change C runtime library to "multi-threaded DLL",
-# otherwise it will be set to "multi-threaded static" when MSVC upgrades
-# the project file to MSVC 2005/2008. vladl of Google Test says gtest will
-# probably change their default to match, then this will be unnecessary.
-# One of these mappings converts the debug configuration and the other
-# converts the release configuration. I don't know which is which.
-sed -i -e 's/RuntimeLibrary="5"/RuntimeLibrary="3"/g;
- s/RuntimeLibrary="4"/RuntimeLibrary="2"/g;' gtest/msvc/*.vcproj
-
# TODO(kenton): Remove the ",no-obsolete" part and fix the resulting warnings.
autoreconf -f -i -Wall,no-obsolete
diff --git a/cmake/README.md b/cmake/README.md
index 1d5c8bc1..0abe078e 100644
--- a/cmake/README.md
+++ b/cmake/README.md
@@ -5,17 +5,17 @@ on your computer before proceeding.
Compiling and Installing
========================
-1. Check whether a gtest directory exists in the upper level directory. If you
- checkout the code from github via "git clone", this gtest directory won't
+1. Check whether a gmock directory exists in the upper level directory. If you
+ checkout the code from github via "git clone", this gmock directory won't
exist and you won't be able to build protobuf unit-tests. Consider using one
of the release tar balls instead:
https://github.com/google/protobuf/releases
These release tar balls are more stable versions of protobuf and already
- have the gtest directory included.
+ have the gmock directory included.
- You can also download gtest by yourself and put it in the right place.
+ You can also download gmock by yourself and put it in the right place.
If you absolutely don't want to build and run protobuf unit-tests, skip
this step and use protobuf at your own risk.
@@ -29,7 +29,7 @@ Compiling and Installing
$ cd build
$ cmake -G "Visual Studio 9 2008" ..
- If you don't have gtest, skip the build of tests by turning off the
+ If you don't have gmock, skip the build of tests by turning off the
BUILD_TESTING option:
$ cmake -G "Visutal Studio 9 2008" -DBUILD_TESTING=OFF ..
diff --git a/cmake/extract_includes.bat.in b/cmake/extract_includes.bat.in
index b2e9444d..21c95748 100644
--- a/cmake/extract_includes.bat.in
+++ b/cmake/extract_includes.bat.in
@@ -11,6 +11,7 @@ mkdir include\google\protobuf\compiler\python
mkdir include\google\protobuf\compiler\ruby
mkdir include\google\protobuf\io
mkdir include\google\protobuf\stubs
+mkdir include\google\protobuf\util
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.h include\google\protobuf\any.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.pb.h include\google\protobuf\any.pb.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\api.pb.h include\google\protobuf\api.pb.h
@@ -88,6 +89,7 @@ copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\common.h include
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\fastmem.h include\google\protobuf\stubs\fastmem.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\hash.h include\google\protobuf\stubs\hash.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\once.h include\google\protobuf\stubs\once.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\pbconfig.h include\google\protobuf\stubs\pbconfig.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\platform_macros.h include\google\protobuf\stubs\platform_macros.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\shared_ptr.h include\google\protobuf\stubs\shared_ptr.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\singleton.h include\google\protobuf\stubs\singleton.h
@@ -98,6 +100,11 @@ copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\text_format.h include\
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\timestamp.pb.h include\google\protobuf\timestamp.pb.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\type.pb.h include\google\protobuf\type.pb.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\unknown_field_set.h include\google\protobuf\unknown_field_set.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\field_comparator.h include\google\protobuf\util\field_comparator.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\json_util.h include\google\protobuf\util\json_util.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\message_differencer.h include\google\protobuf\util\message_differencer.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\type_resolver.h include\google\protobuf\util\type_resolver.h
+copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\type_resolver_util.h include\google\protobuf\util\type_resolver_util.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\wire_format.h include\google\protobuf\wire_format.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\wire_format_lite.h include\google\protobuf\wire_format_lite.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\wire_format_lite_inl.h include\google\protobuf\wire_format_lite_inl.h
diff --git a/cmake/libprotobuf-lite.cmake b/cmake/libprotobuf-lite.cmake
index 32c2d026..db55ea92 100644
--- a/cmake/libprotobuf-lite.cmake
+++ b/cmake/libprotobuf-lite.cmake
@@ -10,9 +10,15 @@ set(libprotobuf_lite_files
${protobuf_source_dir}/src/google/protobuf/repeated_field.cc
${protobuf_source_dir}/src/google/protobuf/stubs/atomicops_internals_x86_gcc.cc
${protobuf_source_dir}/src/google/protobuf/stubs/atomicops_internals_x86_msvc.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/bytestream.cc
${protobuf_source_dir}/src/google/protobuf/stubs/common.cc
${protobuf_source_dir}/src/google/protobuf/stubs/once.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/status.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/statusor.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/stringpiece.cc
${protobuf_source_dir}/src/google/protobuf/stubs/stringprintf.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/strutil.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/time.cc
${protobuf_source_dir}/src/google/protobuf/wire_format_lite.cc
)
diff --git a/cmake/libprotobuf.cmake b/cmake/libprotobuf.cmake
index b1f2dc3e..53ba3d3e 100644
--- a/cmake/libprotobuf.cmake
+++ b/cmake/libprotobuf.cmake
@@ -24,13 +24,30 @@ set(libprotobuf_files
${protobuf_source_dir}/src/google/protobuf/service.cc
${protobuf_source_dir}/src/google/protobuf/source_context.pb.cc
${protobuf_source_dir}/src/google/protobuf/struct.pb.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/mathlimits.cc
${protobuf_source_dir}/src/google/protobuf/stubs/structurally_valid.cc
- ${protobuf_source_dir}/src/google/protobuf/stubs/strutil.cc
${protobuf_source_dir}/src/google/protobuf/stubs/substitute.cc
${protobuf_source_dir}/src/google/protobuf/text_format.cc
${protobuf_source_dir}/src/google/protobuf/timestamp.pb.cc
${protobuf_source_dir}/src/google/protobuf/type.pb.cc
${protobuf_source_dir}/src/google/protobuf/unknown_field_set.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/field_comparator.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/datapiece.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/default_value_objectwriter.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/error_listener.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/field_mask_utility.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/json_escaping.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/json_objectwriter.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/json_stream_parser.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/object_writer.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info_test_helper.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/utility.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/json_util.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/message_differencer.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/type_resolver_util.cc
${protobuf_source_dir}/src/google/protobuf/wire_format.cc
${protobuf_source_dir}/src/google/protobuf/wrappers.pb.cc
)
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
index 8fb5eef1..16c94933 100644
--- a/cmake/tests.cmake
+++ b/cmake/tests.cmake
@@ -1,10 +1,20 @@
+if (NOT EXISTS "${PROJECT_SOURCE_DIR}/../gmock/CMakeLists.txt")
+ message(FATAL_ERROR "Cannot find gmock directory.")
+endif()
+
include_directories(
- ${protobuf_source_dir}/gtest/include
- ${protobuf_source_dir}/gtest)
+ ${protobuf_source_dir}/gmock
+ ${protobuf_source_dir}/gmock/gtest
+ ${protobuf_source_dir}/gmock/gtest/include
+ ${protobuf_source_dir}/gmock/include
+)
-add_library(gtest STATIC ${protobuf_source_dir}/gtest/src/gtest-all.cc)
-add_library(gtest_main STATIC ${protobuf_source_dir}/gtest/src/gtest_main.cc)
-target_link_libraries(gtest_main gtest)
+add_library(gmock STATIC
+ ${protobuf_source_dir}/gmock/src/gmock-all.cc
+ ${protobuf_source_dir}/gmock/gtest/src/gtest-all.cc
+)
+add_library(gmock_main STATIC ${protobuf_source_dir}/gmock/src/gmock_main.cc)
+target_link_libraries(gmock_main gmock)
set(lite_test_protos
google/protobuf/map_lite_unittest.proto
@@ -39,6 +49,15 @@ set(tests_protos
google/protobuf/unittest_preserve_unknown_enum2.proto
google/protobuf/unittest_proto3_arena.proto
google/protobuf/unittest_well_known_types.proto
+ google/protobuf/util/internal/testdata/anys.proto
+ google/protobuf/util/internal/testdata/books.proto
+ google/protobuf/util/internal/testdata/default_value.proto
+ google/protobuf/util/internal/testdata/default_value_test.proto
+ google/protobuf/util/internal/testdata/field_mask.proto
+ google/protobuf/util/internal/testdata/maps.proto
+ google/protobuf/util/internal/testdata/struct.proto
+ google/protobuf/util/internal/testdata/timestamp_duration.proto
+ google/protobuf/util/json_format_proto3.proto
)
macro(compile_proto_file filename)
@@ -46,10 +65,10 @@ macro(compile_proto_file filename)
get_filename_component(basename ${filename} NAME_WE)
add_custom_command(
OUTPUT ${protobuf_source_dir}/src/${dirname}/${basename}.pb.cc
+ DEPENDS protoc ${protobuf_source_dir}/src/${dirname}/${basename}.proto
COMMAND protoc ${protobuf_source_dir}/src/${dirname}/${basename}.proto
--proto_path=${protobuf_source_dir}/src
--cpp_out=${protobuf_source_dir}/src
- DEPENDS protoc
)
endmacro(compile_proto_file)
@@ -113,21 +132,35 @@ set(tests_files
${protobuf_source_dir}/src/google/protobuf/reflection_ops_unittest.cc
${protobuf_source_dir}/src/google/protobuf/repeated_field_reflection_unittest.cc
${protobuf_source_dir}/src/google/protobuf/repeated_field_unittest.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/bytestream_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/common_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/once_unittest.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/status_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/statusor_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/stringpiece_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/stringprintf_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/structurally_valid_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/strutil_unittest.cc
${protobuf_source_dir}/src/google/protobuf/stubs/template_util_unittest.cc
+ ${protobuf_source_dir}/src/google/protobuf/stubs/time_test.cc
${protobuf_source_dir}/src/google/protobuf/stubs/type_traits_unittest.cc
${protobuf_source_dir}/src/google/protobuf/text_format_unittest.cc
${protobuf_source_dir}/src/google/protobuf/unknown_field_set_unittest.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/field_comparator_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/json_objectwriter_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/json_stream_parser_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info_test_helper.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/json_util_test.cc
+ ${protobuf_source_dir}/src/google/protobuf/util/type_resolver_util_test.cc
${protobuf_source_dir}/src/google/protobuf/well_known_types_unittest.cc
${protobuf_source_dir}/src/google/protobuf/wire_format_unittest.cc
)
add_executable(tests ${tests_files} ${common_test_files} ${tests_proto_files} ${lite_test_proto_files})
-target_link_libraries(tests libprotoc libprotobuf gtest_main)
+target_link_libraries(tests libprotoc libprotobuf gmock_main)
set(test_plugin_files
${protobuf_source_dir}/src/google/protobuf/compiler/mock_code_generator.cc
@@ -137,7 +170,7 @@ set(test_plugin_files
)
add_executable(test_plugin ${test_plugin_files})
-target_link_libraries(test_plugin libprotoc libprotobuf gtest)
+target_link_libraries(test_plugin libprotoc libprotobuf gmock)
set(lite_test_files
${protobuf_source_dir}/src/google/protobuf/arena_test_util.cc
diff --git a/configure.ac b/configure.ac
index b36fa0c0..ef5342ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -163,12 +163,12 @@ case "$target_os" in
;;
esac
-# HACK: Make gtest's configure script pick up our copy of CFLAGS and CXXFLAGS,
-# since the flags added by ACX_CHECK_SUNCC must be used when compiling gtest
+# HACK: Make gmock's configure script pick up our copy of CFLAGS and CXXFLAGS,
+# since the flags added by ACX_CHECK_SUNCC must be used when compiling gmock
# too.
export CFLAGS
export CXXFLAGS
-AC_CONFIG_SUBDIRS([gtest])
+AC_CONFIG_SUBDIRS([gmock])
AC_CONFIG_FILES([Makefile src/Makefile conformance/Makefile protobuf.pc protobuf-lite.pc])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index 67cd4c57..5b28579f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -147,7 +147,12 @@ nobase_include_HEADERS = \
google/protobuf/compiler/objectivec/objectivec_helpers.h \
google/protobuf/compiler/python/python_generator.h \
google/protobuf/compiler/ruby/ruby_generator.h \
- google/protobuf/compiler/csharp/csharp_generator.h
+ google/protobuf/compiler/csharp/csharp_generator.h \
+ google/protobuf/util/type_resolver.h \
+ google/protobuf/util/type_resolver_util.h \
+ google/protobuf/util/json_util.h \
+ google/protobuf/util/field_comparator.h \
+ google/protobuf/util/message_differencer.h
lib_LTLIBRARIES = libprotobuf-lite.la libprotobuf.la libprotoc.la
@@ -156,13 +161,27 @@ libprotobuf_lite_la_LDFLAGS = -version-info 10:0:0 -export-dynamic -no-undefined
libprotobuf_lite_la_SOURCES = \
google/protobuf/stubs/atomicops_internals_x86_gcc.cc \
google/protobuf/stubs/atomicops_internals_x86_msvc.cc \
+ google/protobuf/stubs/bytestream.cc \
+ google/protobuf/stubs/bytestream.h \
google/protobuf/stubs/common.cc \
- google/protobuf/stubs/once.cc \
google/protobuf/stubs/hash.h \
google/protobuf/stubs/map_util.h \
+ google/protobuf/stubs/mathutil.h \
+ google/protobuf/stubs/once.cc \
google/protobuf/stubs/shared_ptr.h \
+ google/protobuf/stubs/status.cc \
+ google/protobuf/stubs/status.h \
+ google/protobuf/stubs/status_macros.h \
+ google/protobuf/stubs/statusor.cc \
+ google/protobuf/stubs/statusor.h \
+ google/protobuf/stubs/stringpiece.cc \
+ google/protobuf/stubs/stringpiece.h \
google/protobuf/stubs/stringprintf.cc \
google/protobuf/stubs/stringprintf.h \
+ google/protobuf/stubs/strutil.cc \
+ google/protobuf/stubs/strutil.h \
+ google/protobuf/stubs/time.cc \
+ google/protobuf/stubs/time.h \
google/protobuf/arena.cc \
google/protobuf/arenastring.cc \
google/protobuf/extension_set.cc \
@@ -181,6 +200,8 @@ libprotobuf_la_SOURCES = \
$(libprotobuf_lite_la_SOURCES) \
google/protobuf/any.pb.cc \
google/protobuf/api.pb.cc \
+ google/protobuf/stubs/mathlimits.h \
+ google/protobuf/stubs/mathlimits.cc \
google/protobuf/any.cc \
google/protobuf/descriptor.cc \
google/protobuf/descriptor_database.cc \
@@ -199,8 +220,6 @@ libprotobuf_la_SOURCES = \
google/protobuf/source_context.pb.cc \
google/protobuf/struct.pb.cc \
google/protobuf/stubs/structurally_valid.cc \
- google/protobuf/stubs/strutil.cc \
- google/protobuf/stubs/strutil.h \
google/protobuf/stubs/substitute.cc \
google/protobuf/stubs/substitute.h \
google/protobuf/text_format.cc \
@@ -215,7 +234,46 @@ libprotobuf_la_SOURCES = \
google/protobuf/io/tokenizer.cc \
google/protobuf/io/zero_copy_stream_impl.cc \
google/protobuf/compiler/importer.cc \
- google/protobuf/compiler/parser.cc
+ google/protobuf/compiler/parser.cc \
+ google/protobuf/util/field_comparator.cc \
+ google/protobuf/util/internal/constants.h \
+ google/protobuf/util/internal/datapiece.cc \
+ google/protobuf/util/internal/datapiece.h \
+ google/protobuf/util/internal/default_value_objectwriter.cc \
+ google/protobuf/util/internal/default_value_objectwriter.h \
+ google/protobuf/util/internal/error_listener.cc \
+ google/protobuf/util/internal/error_listener.h \
+ google/protobuf/util/internal/expecting_objectwriter.h \
+ google/protobuf/util/internal/field_mask_utility.cc \
+ google/protobuf/util/internal/field_mask_utility.h \
+ google/protobuf/util/internal/json_escaping.cc \
+ google/protobuf/util/internal/json_escaping.h \
+ google/protobuf/util/internal/json_objectwriter.cc \
+ google/protobuf/util/internal/json_objectwriter.h \
+ google/protobuf/util/internal/json_stream_parser.cc \
+ google/protobuf/util/internal/json_stream_parser.h \
+ google/protobuf/util/internal/location_tracker.h \
+ google/protobuf/util/internal/mock_error_listener.h \
+ google/protobuf/util/internal/object_location_tracker.h \
+ google/protobuf/util/internal/object_source.h \
+ google/protobuf/util/internal/object_writer.cc \
+ google/protobuf/util/internal/object_writer.h \
+ google/protobuf/util/internal/protostream_objectsource.cc \
+ google/protobuf/util/internal/protostream_objectsource.h \
+ google/protobuf/util/internal/protostream_objectwriter.cc \
+ google/protobuf/util/internal/protostream_objectwriter.h \
+ google/protobuf/util/internal/snake2camel_objectwriter.h \
+ google/protobuf/util/internal/structured_objectwriter.h \
+ google/protobuf/util/internal/testdata \
+ google/protobuf/util/internal/type_info.cc \
+ google/protobuf/util/internal/type_info.h \
+ google/protobuf/util/internal/type_info_test_helper.cc \
+ google/protobuf/util/internal/type_info_test_helper.h \
+ google/protobuf/util/internal/utility.cc \
+ google/protobuf/util/internal/utility.h \
+ google/protobuf/util/json_util.cc \
+ google/protobuf/util/type_resolver_util.cc \
+ google/protobuf/util/message_differencer.cc
nodist_libprotobuf_la_SOURCES = $(nodist_libprotobuf_lite_la_SOURCES)
libprotoc_la_LIBADD = $(PTHREAD_LIBS) libprotobuf.la
@@ -394,35 +452,44 @@ protoc_SOURCES = google/protobuf/compiler/main.cc
# Tests ==============================================================
-protoc_inputs = \
- google/protobuf/any_test.proto \
- google/protobuf/map_lite_unittest.proto \
- google/protobuf/map_proto2_unittest.proto \
- google/protobuf/map_unittest.proto \
- google/protobuf/unittest.proto \
- google/protobuf/unittest_arena.proto \
- google/protobuf/unittest_custom_options.proto \
- google/protobuf/unittest_drop_unknown_fields.proto \
- google/protobuf/unittest_embed_optimize_for.proto \
- google/protobuf/unittest_empty.proto \
- google/protobuf/unittest_enormous_descriptor.proto \
- google/protobuf/unittest_import_lite.proto \
- google/protobuf/unittest_import.proto \
- google/protobuf/unittest_import_public_lite.proto \
- google/protobuf/unittest_import_public.proto \
- google/protobuf/unittest_lite_imports_nonlite.proto \
- google/protobuf/unittest_lite.proto \
- google/protobuf/unittest_mset.proto \
- google/protobuf/unittest_no_arena_import.proto \
- google/protobuf/unittest_no_arena.proto \
- google/protobuf/unittest_no_field_presence.proto \
- google/protobuf/unittest_no_generic_services.proto \
- google/protobuf/unittest_optimize_for.proto \
- google/protobuf/unittest_preserve_unknown_enum.proto \
- google/protobuf/unittest_preserve_unknown_enum2.proto \
- google/protobuf/unittest_proto3_arena.proto \
- google/protobuf/unittest_well_known_types.proto \
- google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto \
+protoc_inputs = \
+ google/protobuf/any_test.proto \
+ google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto \
+ google/protobuf/map_lite_unittest.proto \
+ google/protobuf/map_proto2_unittest.proto \
+ google/protobuf/map_unittest.proto \
+ google/protobuf/unittest_arena.proto \
+ google/protobuf/unittest_custom_options.proto \
+ google/protobuf/unittest_drop_unknown_fields.proto \
+ google/protobuf/unittest_embed_optimize_for.proto \
+ google/protobuf/unittest_empty.proto \
+ google/protobuf/unittest_enormous_descriptor.proto \
+ google/protobuf/unittest_import_lite.proto \
+ google/protobuf/unittest_import.proto \
+ google/protobuf/unittest_import_public_lite.proto \
+ google/protobuf/unittest_import_public.proto \
+ google/protobuf/unittest_lite_imports_nonlite.proto \
+ google/protobuf/unittest_lite.proto \
+ google/protobuf/unittest_mset.proto \
+ google/protobuf/unittest_no_arena_import.proto \
+ google/protobuf/unittest_no_arena.proto \
+ google/protobuf/unittest_no_field_presence.proto \
+ google/protobuf/unittest_no_generic_services.proto \
+ google/protobuf/unittest_optimize_for.proto \
+ google/protobuf/unittest_preserve_unknown_enum2.proto \
+ google/protobuf/unittest_preserve_unknown_enum.proto \
+ google/protobuf/unittest.proto \
+ google/protobuf/unittest_proto3_arena.proto \
+ google/protobuf/unittest_well_known_types.proto \
+ google/protobuf/util/internal/testdata/anys.proto \
+ google/protobuf/util/internal/testdata/books.proto \
+ google/protobuf/util/internal/testdata/default_value.proto \
+ google/protobuf/util/internal/testdata/default_value_test.proto \
+ google/protobuf/util/internal/testdata/field_mask.proto \
+ google/protobuf/util/internal/testdata/maps.proto \
+ google/protobuf/util/internal/testdata/struct.proto \
+ google/protobuf/util/internal/testdata/timestamp_duration.proto \
+ google/protobuf/util/json_format_proto3.proto \
google/protobuf/compiler/cpp/cpp_test_large_enum_value.proto
EXTRA_DIST = \
@@ -459,58 +526,76 @@ protoc_lite_outputs = \
google/protobuf/unittest_import_public_lite.pb.cc \
google/protobuf/unittest_import_public_lite.pb.h
-protoc_outputs = \
- $(protoc_lite_outputs) \
- google/protobuf/any_test.pb.cc \
- google/protobuf/any_test.pb.h \
- google/protobuf/map_proto2_unittest.pb.cc \
- google/protobuf/map_proto2_unittest.pb.h \
- google/protobuf/map_unittest.pb.cc \
- google/protobuf/map_unittest.pb.h \
- google/protobuf/unittest.pb.cc \
- google/protobuf/unittest.pb.h \
- google/protobuf/unittest_arena.pb.cc \
- google/protobuf/unittest_arena.pb.h \
- google/protobuf/unittest_custom_options.pb.cc \
- google/protobuf/unittest_custom_options.pb.h \
- google/protobuf/unittest_drop_unknown_fields.pb.cc \
- google/protobuf/unittest_drop_unknown_fields.pb.h \
- google/protobuf/unittest_embed_optimize_for.pb.cc \
- google/protobuf/unittest_embed_optimize_for.pb.h \
- google/protobuf/unittest_empty.pb.cc \
- google/protobuf/unittest_empty.pb.h \
- google/protobuf/unittest_enormous_descriptor.pb.cc \
- google/protobuf/unittest_enormous_descriptor.pb.h \
- google/protobuf/unittest_import.pb.cc \
- google/protobuf/unittest_import.pb.h \
- google/protobuf/unittest_import_public.pb.cc \
- google/protobuf/unittest_import_public.pb.h \
- google/protobuf/unittest_lite_imports_nonlite.pb.cc \
- google/protobuf/unittest_lite_imports_nonlite.pb.h \
- google/protobuf/unittest_mset.pb.cc \
- google/protobuf/unittest_mset.pb.h \
- google/protobuf/unittest_no_arena.pb.cc \
- google/protobuf/unittest_no_arena.pb.h \
- google/protobuf/unittest_no_arena_import.pb.cc \
- google/protobuf/unittest_no_arena_import.pb.h \
- google/protobuf/unittest_no_field_presence.pb.cc \
- google/protobuf/unittest_no_field_presence.pb.h \
- google/protobuf/unittest_no_generic_services.pb.cc \
- google/protobuf/unittest_no_generic_services.pb.h \
- google/protobuf/unittest_optimize_for.pb.cc \
- google/protobuf/unittest_optimize_for.pb.h \
- google/protobuf/unittest_preserve_unknown_enum.pb.cc \
- google/protobuf/unittest_preserve_unknown_enum.pb.h \
- google/protobuf/unittest_preserve_unknown_enum2.pb.cc \
- google/protobuf/unittest_preserve_unknown_enum2.pb.h \
- google/protobuf/unittest_proto3_arena.pb.cc \
- google/protobuf/unittest_proto3_arena.pb.h \
- google/protobuf/unittest_well_known_types.pb.cc \
- google/protobuf/unittest_well_known_types.pb.h \
- google/protobuf/compiler/cpp/cpp_test_large_enum_value.pb.cc \
- google/protobuf/compiler/cpp/cpp_test_large_enum_value.pb.h \
- google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.cc \
- google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h
+protoc_outputs = \
+ $(protoc_lite_outputs) \
+ google/protobuf/any_test.pb.cc \
+ google/protobuf/any_test.pb.h \
+ google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.cc \
+ google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h \
+ google/protobuf/compiler/cpp/cpp_test_large_enum_value.pb.cc \
+ google/protobuf/compiler/cpp/cpp_test_large_enum_value.pb.h \
+ google/protobuf/map_proto2_unittest.pb.cc \
+ google/protobuf/map_proto2_unittest.pb.h \
+ google/protobuf/map_unittest.pb.cc \
+ google/protobuf/map_unittest.pb.h \
+ google/protobuf/unittest_arena.pb.cc \
+ google/protobuf/unittest_arena.pb.h \
+ google/protobuf/unittest_custom_options.pb.cc \
+ google/protobuf/unittest_custom_options.pb.h \
+ google/protobuf/unittest_drop_unknown_fields.pb.cc \
+ google/protobuf/unittest_drop_unknown_fields.pb.h \
+ google/protobuf/unittest_embed_optimize_for.pb.cc \
+ google/protobuf/unittest_embed_optimize_for.pb.h \
+ google/protobuf/unittest_empty.pb.cc \
+ google/protobuf/unittest_empty.pb.h \
+ google/protobuf/unittest_enormous_descriptor.pb.cc \
+ google/protobuf/unittest_enormous_descriptor.pb.h \
+ google/protobuf/unittest_import.pb.cc \
+ google/protobuf/unittest_import.pb.h \
+ google/protobuf/unittest_import_public.pb.cc \
+ google/protobuf/unittest_import_public.pb.h \
+ google/protobuf/unittest_lite_imports_nonlite.pb.cc \
+ google/protobuf/unittest_lite_imports_nonlite.pb.h \
+ google/protobuf/unittest_mset.pb.cc \
+ google/protobuf/unittest_mset.pb.h \
+ google/protobuf/unittest_no_arena_import.pb.cc \
+ google/protobuf/unittest_no_arena_import.pb.h \
+ google/protobuf/unittest_no_arena.pb.cc \
+ google/protobuf/unittest_no_arena.pb.h \
+ google/protobuf/unittest_no_field_presence.pb.cc \
+ google/protobuf/unittest_no_field_presence.pb.h \
+ google/protobuf/unittest_no_generic_services.pb.cc \
+ google/protobuf/unittest_no_generic_services.pb.h \
+ google/protobuf/unittest_optimize_for.pb.cc \
+ google/protobuf/unittest_optimize_for.pb.h \
+ google/protobuf/unittest.pb.cc \
+ google/protobuf/unittest.pb.h \
+ google/protobuf/unittest_preserve_unknown_enum2.pb.cc \
+ google/protobuf/unittest_preserve_unknown_enum2.pb.h \
+ google/protobuf/unittest_preserve_unknown_enum.pb.cc \
+ google/protobuf/unittest_preserve_unknown_enum.pb.h \
+ google/protobuf/unittest_proto3_arena.pb.cc \
+ google/protobuf/unittest_proto3_arena.pb.h \
+ google/protobuf/unittest_well_known_types.pb.cc \
+ google/protobuf/unittest_well_known_types.pb.h \
+ google/protobuf/util/internal/testdata/anys.pb.cc \
+ google/protobuf/util/internal/testdata/anys.pb.h \
+ google/protobuf/util/internal/testdata/books.pb.cc \
+ google/protobuf/util/internal/testdata/books.pb.h \
+ google/protobuf/util/internal/testdata/default_value.pb.cc \
+ google/protobuf/util/internal/testdata/default_value.pb.h \
+ google/protobuf/util/internal/testdata/default_value_test.pb.cc \
+ google/protobuf/util/internal/testdata/default_value_test.pb.h \
+ google/protobuf/util/internal/testdata/field_mask.pb.cc \
+ google/protobuf/util/internal/testdata/field_mask.pb.h \
+ google/protobuf/util/internal/testdata/maps.pb.cc \
+ google/protobuf/util/internal/testdata/maps.pb.h \
+ google/protobuf/util/internal/testdata/struct.pb.cc \
+ google/protobuf/util/internal/testdata/struct.pb.h \
+ google/protobuf/util/internal/testdata/timestamp_duration.pb.cc \
+ google/protobuf/util/internal/testdata/timestamp_duration.pb.h \
+ google/protobuf/util/json_format_proto3.pb.cc \
+ google/protobuf/util/json_format_proto3.pb.h
BUILT_SOURCES = $(protoc_outputs)
@@ -549,21 +634,27 @@ COMMON_TEST_SOURCES = \
check_PROGRAMS = protoc protobuf-test protobuf-lazy-descriptor-test \
protobuf-lite-test test_plugin $(GZCHECKPROGRAMS)
protobuf_test_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la \
- $(top_builddir)/gtest/lib/libgtest.la \
- $(top_builddir)/gtest/lib/libgtest_main.la
-protobuf_test_CPPFLAGS = -I$(top_srcdir)/gtest/include \
- -I$(top_builddir)/gtest/include
+ ../gmock/gtest/lib/libgtest.la \
+ ../gmock/lib/libgmock.la \
+ ../gmock/lib/libgmock_main.la
+protobuf_test_CPPFLAGS = -I$(srcdir)/../gmock/gtest/include \
+ -I$(srcdir)/../gmock/include
# Disable optimization for tests unless the user explicitly asked for it,
# since test_util.cc takes forever to compile with optimization (with GCC).
# See configure.ac for more info.
protobuf_test_CXXFLAGS = $(NO_OPT_CXXFLAGS)
protobuf_test_SOURCES = \
+ google/protobuf/stubs/bytestream_unittest.cc \
google/protobuf/stubs/common_unittest.cc \
google/protobuf/stubs/once_unittest.cc \
- google/protobuf/stubs/strutil_unittest.cc \
- google/protobuf/stubs/structurally_valid_unittest.cc \
+ google/protobuf/stubs/statusor_test.cc \
+ google/protobuf/stubs/status_test.cc \
+ google/protobuf/stubs/stringpiece_unittest.cc \
google/protobuf/stubs/stringprintf_unittest.cc \
+ google/protobuf/stubs/structurally_valid_unittest.cc \
+ google/protobuf/stubs/strutil_unittest.cc \
google/protobuf/stubs/template_util_unittest.cc \
+ google/protobuf/stubs/time_test.cc \
google/protobuf/stubs/type_traits_unittest.cc \
google/protobuf/any_test.cc \
google/protobuf/arenastring_unittest.cc \
@@ -606,16 +697,28 @@ protobuf_test_SOURCES = \
google/protobuf/compiler/python/python_plugin_unittest.cc \
google/protobuf/compiler/ruby/ruby_generator_unittest.cc \
google/protobuf/compiler/csharp/csharp_generator_unittest.cc \
+ google/protobuf/util/field_comparator_test.cc \
+ google/protobuf/util/internal/default_value_objectwriter_test.cc \
+ google/protobuf/util/internal/json_objectwriter_test.cc \
+ google/protobuf/util/internal/json_stream_parser_test.cc \
+ google/protobuf/util/internal/protostream_objectsource_test.cc \
+ google/protobuf/util/internal/protostream_objectwriter_test.cc \
+ google/protobuf/util/internal/type_info_test_helper.cc \
+ google/protobuf/util/json_util_test.cc \
+ google/protobuf/util/type_resolver_util_test.cc \
$(COMMON_TEST_SOURCES)
+
+
nodist_protobuf_test_SOURCES = $(protoc_outputs)
# Run cpp_unittest again with PROTOBUF_TEST_NO_DESCRIPTORS defined.
protobuf_lazy_descriptor_test_LDADD = $(PTHREAD_LIBS) libprotobuf.la \
libprotoc.la \
- $(top_builddir)/gtest/lib/libgtest.la \
- $(top_builddir)/gtest/lib/libgtest_main.la
-protobuf_lazy_descriptor_test_CPPFLAGS = -I$(top_srcdir)/gtest/include \
- -I$(top_builddir)/gtest/include \
+ ../gmock/gtest/lib/libgtest.la \
+ ../gmock/lib/libgmock.la \
+ ../gmock/lib/libgmock_main.la
+protobuf_lazy_descriptor_test_CPPFLAGS = -I$(srcdir)/../gmock/include \
+ -I$(srcdir)/../gmock/gtest/include \
-DPROTOBUF_TEST_NO_DESCRIPTORS
protobuf_lazy_descriptor_test_CXXFLAGS = $(NO_OPT_CXXFLAGS)
protobuf_lazy_descriptor_test_SOURCES = \
@@ -640,9 +743,8 @@ nodist_protobuf_lite_test_SOURCES = $(protoc_lite_outputs)
# Test plugin binary.
test_plugin_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la \
- $(top_builddir)/gtest/lib/libgtest.la
-test_plugin_CPPFLAGS = -I$(top_srcdir)/gtest/include \
- -I$(top_builddir)/gtest/include
+ ../gmock/gtest/lib/libgtest.la
+test_plugin_CPPFLAGS = -I$(srcdir)/../gmock/gtest/include
test_plugin_SOURCES = \
google/protobuf/compiler/mock_code_generator.cc \
google/protobuf/testing/file.cc \
diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc
index af85919a..17f67a7b 100644
--- a/src/google/protobuf/compiler/cpp/cpp_message.cc
+++ b/src/google/protobuf/compiler/cpp/cpp_message.cc
@@ -621,7 +621,7 @@ GenerateSingularFieldHasBits(const FieldDescriptor* field,
// has_$name$() methods.
vars["has_array_index"] = SimpleItoa(field->index() / 32);
vars["has_mask"] = StrCat(strings::Hex(1u << (field->index() % 32),
- strings::Hex::ZERO_PAD_8));
+ strings::ZERO_PAD_8));
printer->Print(vars,
"$inline$"
"bool $classname$::has_$name$() const {\n"
@@ -3364,7 +3364,7 @@ static string ConditionalToCheckBitmasks(const vector<uint32>& masks) {
vector<string> parts;
for (int i = 0; i < masks.size(); i++) {
if (masks[i] == 0) continue;
- string m = StrCat("0x", strings::Hex(masks[i], strings::Hex::ZERO_PAD_8));
+ string m = StrCat("0x", strings::Hex(masks[i], strings::ZERO_PAD_8));
// Each xor evaluates to 0 if the expected bits are present.
parts.push_back(StrCat("((_has_bits_[", i, "] & ", m, ") ^ ", m, ")"));
}
@@ -3659,7 +3659,7 @@ GenerateIsInitialized(io::Printer* printer) {
printer->Print(
"if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n",
"i", SimpleItoa(i),
- "mask", StrCat(strings::Hex(mask, strings::Hex::ZERO_PAD_8)));
+ "mask", StrCat(strings::Hex(mask, strings::ZERO_PAD_8)));
}
}
}
diff --git a/src/google/protobuf/stubs/bytestream.cc b/src/google/protobuf/stubs/bytestream.cc
new file mode 100644
index 00000000..f4af6a50
--- /dev/null
+++ b/src/google/protobuf/stubs/bytestream.cc
@@ -0,0 +1,196 @@
+// 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 <google/protobuf/stubs/bytestream.h>
+
+#include <string.h>
+#include <algorithm>
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+void ByteSource::CopyTo(ByteSink* sink, size_t n) {
+ while (n > 0) {
+ StringPiece fragment = Peek();
+ if (fragment.empty()) {
+ GOOGLE_LOG(DFATAL) << "ByteSource::CopyTo() overran input.";
+ break;
+ }
+ std::size_t fragment_size = std::min<std::size_t>(n, fragment.size());
+ sink->Append(fragment.data(), fragment_size);
+ Skip(fragment_size);
+ n -= fragment_size;
+ }
+}
+
+void ByteSink::Flush() {}
+
+void UncheckedArrayByteSink::Append(const char* data, size_t n) {
+ if (data != dest_) {
+ // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+ GOOGLE_DCHECK(!(dest_ <= data && data < (dest_ + n)))
+ << "Append() data[] overlaps with dest_[]";
+ memcpy(dest_, data, n);
+ }
+ dest_ += n;
+}
+
+CheckedArrayByteSink::CheckedArrayByteSink(char* outbuf, size_t capacity)
+ : outbuf_(outbuf), capacity_(capacity), size_(0), overflowed_(false) {
+}
+
+void CheckedArrayByteSink::Append(const char* bytes, size_t n) {
+ size_t available = capacity_ - size_;
+ if (n > available) {
+ n = available;
+ overflowed_ = true;
+ }
+ if (n > 0 && bytes != (outbuf_ + size_)) {
+ // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+ GOOGLE_DCHECK(!(outbuf_ <= bytes && bytes < (outbuf_ + capacity_)))
+ << "Append() bytes[] overlaps with outbuf_[]";
+ memcpy(outbuf_ + size_, bytes, n);
+ }
+ size_ += n;
+}
+
+GrowingArrayByteSink::GrowingArrayByteSink(size_t estimated_size)
+ : capacity_(estimated_size),
+ buf_(new char[estimated_size]),
+ size_(0) {
+}
+
+GrowingArrayByteSink::~GrowingArrayByteSink() {
+ delete[] buf_; // Just in case the user didn't call GetBuffer.
+}
+
+void GrowingArrayByteSink::Append(const char* bytes, size_t n) {
+ size_t available = capacity_ - size_;
+ if (bytes != (buf_ + size_)) {
+ // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+ // We need to test for this before calling Expand() which may reallocate.
+ GOOGLE_DCHECK(!(buf_ <= bytes && bytes < (buf_ + capacity_)))
+ << "Append() bytes[] overlaps with buf_[]";
+ }
+ if (n > available) {
+ Expand(n - available);
+ }
+ if (n > 0 && bytes != (buf_ + size_)) {
+ memcpy(buf_ + size_, bytes, n);
+ }
+ size_ += n;
+}
+
+char* GrowingArrayByteSink::GetBuffer(size_t* nbytes) {
+ ShrinkToFit();
+ char* b = buf_;
+ *nbytes = size_;
+ buf_ = NULL;
+ size_ = capacity_ = 0;
+ return b;
+}
+
+void GrowingArrayByteSink::Expand(size_t amount) { // Expand by at least 50%.
+ size_t new_capacity = std::max(capacity_ + amount, (3 * capacity_) / 2);
+ char* bigger = new char[new_capacity];
+ memcpy(bigger, buf_, size_);
+ delete[] buf_;
+ buf_ = bigger;
+ capacity_ = new_capacity;
+}
+
+void GrowingArrayByteSink::ShrinkToFit() {
+ // Shrink only if the buffer is large and size_ is less than 3/4
+ // of capacity_.
+ if (capacity_ > 256 && size_ < (3 * capacity_) / 4) {
+ char* just_enough = new char[size_];
+ memcpy(just_enough, buf_, size_);
+ delete[] buf_;
+ buf_ = just_enough;
+ capacity_ = size_;
+ }
+}
+
+void StringByteSink::Append(const char* data, size_t n) {
+ dest_->append(data, n);
+}
+
+size_t ArrayByteSource::Available() const {
+ return input_.size();
+}
+
+StringPiece ArrayByteSource::Peek() {
+ return input_;
+}
+
+void ArrayByteSource::Skip(size_t n) {
+ GOOGLE_DCHECK_LE(n, input_.size());
+ input_.remove_prefix(n);
+}
+
+LimitByteSource::LimitByteSource(ByteSource *source, size_t limit)
+ : source_(source),
+ limit_(limit) {
+}
+
+size_t LimitByteSource::Available() const {
+ size_t available = source_->Available();
+ if (available > limit_) {
+ available = limit_;
+ }
+
+ return available;
+}
+
+StringPiece LimitByteSource::Peek() {
+ StringPiece piece(source_->Peek());
+ if (piece.size() > limit_) {
+ piece.set(piece.data(), limit_);
+ }
+
+ return piece;
+}
+
+void LimitByteSource::Skip(size_t n) {
+ GOOGLE_DCHECK_LE(n, limit_);
+ source_->Skip(n);
+ limit_ -= n;
+}
+
+void LimitByteSource::CopyTo(ByteSink *sink, size_t n) {
+ GOOGLE_DCHECK_LE(n, limit_);
+ source_->CopyTo(sink, n);
+ limit_ -= n;
+}
+
+} // namespace strings
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/bytestream.h b/src/google/protobuf/stubs/bytestream.h
new file mode 100644
index 00000000..de8e0204
--- /dev/null
+++ b/src/google/protobuf/stubs/bytestream.h
@@ -0,0 +1,348 @@
+// 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 declares the ByteSink and ByteSource abstract interfaces. These
+// interfaces represent objects that consume (ByteSink) or produce (ByteSource)
+// a sequence of bytes. Using these abstract interfaces in your APIs can help
+// make your code work with a variety of input and output types.
+//
+// This file also declares the following commonly used implementations of these
+// interfaces.
+//
+// ByteSink:
+// UncheckedArrayByteSink Writes to an array, without bounds checking
+// CheckedArrayByteSink Writes to an array, with bounds checking
+// GrowingArrayByteSink Allocates and writes to a growable buffer
+// StringByteSink Writes to an STL string
+// NullByteSink Consumes a never-ending stream of bytes
+//
+// ByteSource:
+// ArrayByteSource Reads from an array or string/StringPiece
+// LimitedByteSource Limits the number of bytes read from an
+
+#ifndef GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
+#define GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
+
+#include <stddef.h>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+class CordByteSink;
+class MemBlock;
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+// An abstract interface for an object that consumes a sequence of bytes. This
+// interface offers 3 different ways to append data, and a Flush() function.
+//
+// Example:
+//
+// string my_data;
+// ...
+// ByteSink* sink = ...
+// sink->Append(my_data.data(), my_data.size());
+// sink->Flush();
+//
+class LIBPROTOBUF_EXPORT ByteSink {
+ public:
+ ByteSink() {}
+ virtual ~ByteSink() {}
+
+ // Appends the "n" bytes starting at "bytes".
+ virtual void Append(const char* bytes, size_t n) = 0;
+
+ // Flushes internal buffers. The default implemenation does nothing. ByteSink
+ // subclasses may use internal buffers that require calling Flush() at the end
+ // of the stream.
+ virtual void Flush();
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSink);
+};
+
+// An abstract interface for an object that produces a fixed-size sequence of
+// bytes.
+//
+// Example:
+//
+// ByteSource* source = ...
+// while (source->Available() > 0) {
+// StringPiece data = source->Peek();
+// ... do something with "data" ...
+// source->Skip(data.length());
+// }
+//
+class LIBPROTOBUF_EXPORT ByteSource {
+ public:
+ ByteSource() {}
+ virtual ~ByteSource() {}
+
+ // Returns the number of bytes left to read from the source. Available()
+ // should decrease by N each time Skip(N) is called. Available() may not
+ // increase. Available() returning 0 indicates that the ByteSource is
+ // exhausted.
+ //
+ // Note: Size() may have been a more appropriate name as it's more
+ // indicative of the fixed-size nature of a ByteSource.
+ virtual size_t Available() const = 0;
+
+ // Returns a StringPiece of the next contiguous region of the source. Does not
+ // reposition the source. The returned region is empty iff Available() == 0.
+ //
+ // The returned region is valid until the next call to Skip() or until this
+ // object is destroyed, whichever occurs first.
+ //
+ // The length of the returned StringPiece will be <= Available().
+ virtual StringPiece Peek() = 0;
+
+ // Skips the next n bytes. Invalidates any StringPiece returned by a previous
+ // call to Peek().
+ //
+ // REQUIRES: Available() >= n
+ virtual void Skip(size_t n) = 0;
+
+ // Writes the next n bytes in this ByteSource to the given ByteSink, and
+ // advances this ByteSource past the copied bytes. The default implementation
+ // of this method just copies the bytes normally, but subclasses might
+ // override CopyTo to optimize certain cases.
+ //
+ // REQUIRES: Available() >= n
+ virtual void CopyTo(ByteSink* sink, size_t n);
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSource);
+};
+
+//
+// Some commonly used implementations of ByteSink
+//
+
+// Implementation of ByteSink that writes to an unsized byte array. No
+// bounds-checking is performed--it is the caller's responsibility to ensure
+// that the destination array is large enough.
+//
+// Example:
+//
+// char buf[10];
+// UncheckedArrayByteSink sink(buf);
+// sink.Append("hi", 2); // OK
+// sink.Append(data, 100); // WOOPS! Overflows buf[10].
+//
+class LIBPROTOBUF_EXPORT UncheckedArrayByteSink : public ByteSink {
+ public:
+ explicit UncheckedArrayByteSink(char* dest) : dest_(dest) {}
+ virtual void Append(const char* data, size_t n);
+
+ // Returns the current output pointer so that a caller can see how many bytes
+ // were produced.
+ //
+ // Note: this method is not part of the ByteSink interface.
+ char* CurrentDestination() const { return dest_; }
+
+ private:
+ char* dest_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UncheckedArrayByteSink);
+};
+
+// Implementation of ByteSink that writes to a sized byte array. This sink will
+// not write more than "capacity" bytes to outbuf. Once "capacity" bytes are
+// appended, subsequent bytes will be ignored and Overflowed() will return true.
+// Overflowed() does not cause a runtime error (i.e., it does not CHECK fail).
+//
+// Example:
+//
+// char buf[10];
+// CheckedArrayByteSink sink(buf, 10);
+// sink.Append("hi", 2); // OK
+// sink.Append(data, 100); // Will only write 8 more bytes
+//
+class LIBPROTOBUF_EXPORT CheckedArrayByteSink : public ByteSink {
+ public:
+ CheckedArrayByteSink(char* outbuf, size_t capacity);
+ virtual void Append(const char* bytes, size_t n);
+
+ // Returns the number of bytes actually written to the sink.
+ size_t NumberOfBytesWritten() const { return size_; }
+
+ // Returns true if any bytes were discarded, i.e., if there was an
+ // attempt to write more than 'capacity' bytes.
+ bool Overflowed() const { return overflowed_; }
+
+ private:
+ char* outbuf_;
+ const size_t capacity_;
+ size_t size_;
+ bool overflowed_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CheckedArrayByteSink);
+};
+
+// Implementation of ByteSink that allocates an internal buffer (a char array)
+// and expands it as needed to accomodate appended data (similar to a string),
+// and allows the caller to take ownership of the internal buffer via the
+// GetBuffer() method. The buffer returned from GetBuffer() must be deleted by
+// the caller with delete[]. GetBuffer() also sets the internal buffer to be
+// empty, and subsequent appends to the sink will create a new buffer. The
+// destructor will free the internal buffer if GetBuffer() was not called.
+//
+// Example:
+//
+// GrowingArrayByteSink sink(10);
+// sink.Append("hi", 2);
+// sink.Append(data, n);
+// const char* buf = sink.GetBuffer(); // Ownership transferred
+// delete[] buf;
+//
+class LIBPROTOBUF_EXPORT GrowingArrayByteSink : public strings::ByteSink {
+ public:
+ explicit GrowingArrayByteSink(size_t estimated_size);
+ virtual ~GrowingArrayByteSink();
+ virtual void Append(const char* bytes, size_t n);
+
+ // Returns the allocated buffer, and sets nbytes to its size. The caller takes
+ // ownership of the buffer and must delete it with delete[].
+ char* GetBuffer(size_t* nbytes);
+
+ private:
+ void Expand(size_t amount);
+ void ShrinkToFit();
+
+ size_t capacity_;
+ char* buf_;
+ size_t size_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GrowingArrayByteSink);
+};
+
+// Implementation of ByteSink that appends to the given string.
+// Existing contents of "dest" are not modified; new data is appended.
+//
+// Example:
+//
+// string dest = "Hello ";
+// StringByteSink sink(&dest);
+// sink.Append("World", 5);
+// assert(dest == "Hello World");
+//
+class LIBPROTOBUF_EXPORT StringByteSink : public ByteSink {
+ public:
+ explicit StringByteSink(string* dest) : dest_(dest) {}
+ virtual void Append(const char* data, size_t n);
+
+ private:
+ string* dest_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringByteSink);
+};
+
+// Implementation of ByteSink that discards all data.
+//
+// Example:
+//
+// NullByteSink sink;
+// sink.Append(data, data.size()); // All data ignored.
+//
+class LIBPROTOBUF_EXPORT NullByteSink : public ByteSink {
+ public:
+ NullByteSink() {}
+ virtual void Append(const char *data, size_t n) {}
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(NullByteSink);
+};
+
+//
+// Some commonly used implementations of ByteSource
+//
+
+// Implementation of ByteSource that reads from a StringPiece.
+//
+// Example:
+//
+// string data = "Hello";
+// ArrayByteSource source(data);
+// assert(source.Available() == 5);
+// assert(source.Peek() == "Hello");
+//
+class LIBPROTOBUF_EXPORT ArrayByteSource : public ByteSource {
+ public:
+ explicit ArrayByteSource(StringPiece s) : input_(s) {}
+
+ virtual size_t Available() const;
+ virtual StringPiece Peek();
+ virtual void Skip(size_t n);
+
+ private:
+ StringPiece input_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayByteSource);
+};
+
+// Implementation of ByteSource that wraps another ByteSource, limiting the
+// number of bytes returned.
+//
+// The caller maintains ownership of the underlying source, and may not use the
+// underlying source while using the LimitByteSource object. The underlying
+// source's pointer is advanced by n bytes every time this LimitByteSource
+// object is advanced by n.
+//
+// Example:
+//
+// string data = "Hello World";
+// ArrayByteSource abs(data);
+// assert(abs.Available() == data.size());
+//
+// LimitByteSource limit(abs, 5);
+// assert(limit.Available() == 5);
+// assert(limit.Peek() == "Hello");
+//
+class LIBPROTOBUF_EXPORT LimitByteSource : public ByteSource {
+ public:
+ // Returns at most "limit" bytes from "source".
+ LimitByteSource(ByteSource* source, size_t limit);
+
+ virtual size_t Available() const;
+ virtual StringPiece Peek();
+ virtual void Skip(size_t n);
+
+ // We override CopyTo so that we can forward to the underlying source, in
+ // case it has an efficient implementation of CopyTo.
+ virtual void CopyTo(ByteSink* sink, size_t n);
+
+ private:
+ ByteSource* source_;
+ size_t limit_;
+};
+
+} // namespace strings
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
diff --git a/src/google/protobuf/stubs/bytestream_unittest.cc b/src/google/protobuf/stubs/bytestream_unittest.cc
new file mode 100644
index 00000000..06f114ab
--- /dev/null
+++ b/src/google/protobuf/stubs/bytestream_unittest.cc
@@ -0,0 +1,146 @@
+// 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 <google/protobuf/stubs/bytestream.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace strings {
+namespace {
+
+// We use this class instead of ArrayByteSource to simulate a ByteSource that
+// contains multiple fragments. ArrayByteSource returns the entire array in
+// one fragment.
+class MockByteSource : public ByteSource {
+ public:
+ MockByteSource(StringPiece data, int block_size)
+ : data_(data), block_size_(block_size) {}
+
+ size_t Available() const { return data_.size(); }
+ StringPiece Peek() {
+ return data_.substr(0, block_size_);
+ }
+ void Skip(size_t n) { data_.remove_prefix(n); }
+
+ private:
+ StringPiece data_;
+ int block_size_;
+};
+
+TEST(ByteSourceTest, CopyTo) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ string str;
+ StringByteSink sink(&str);
+
+ source.CopyTo(&sink, data.size());
+ EXPECT_EQ(data, str);
+}
+
+TEST(ByteSourceTest, CopySubstringTo) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ source.Skip(1);
+ string str;
+ StringByteSink sink(&str);
+
+ source.CopyTo(&sink, data.size() - 2);
+ EXPECT_EQ(data.substr(1, data.size() - 2), str);
+ EXPECT_EQ("!", source.Peek());
+}
+
+TEST(ByteSourceTest, LimitByteSource) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ LimitByteSource limit_source(&source, 6);
+ EXPECT_EQ(6, limit_source.Available());
+ limit_source.Skip(1);
+ EXPECT_EQ(5, limit_source.Available());
+
+ {
+ string str;
+ StringByteSink sink(&str);
+ limit_source.CopyTo(&sink, limit_source.Available());
+ EXPECT_EQ("ello ", str);
+ EXPECT_EQ(0, limit_source.Available());
+ EXPECT_EQ(6, source.Available());
+ }
+
+ {
+ string str;
+ StringByteSink sink(&str);
+ source.CopyTo(&sink, source.Available());
+ EXPECT_EQ("world!", str);
+ EXPECT_EQ(0, source.Available());
+ }
+}
+
+TEST(ByteSourceTest, CopyToStringByteSink) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ string str;
+ StringByteSink sink(&str);
+ source.CopyTo(&sink, data.size());
+ EXPECT_EQ(data, str);
+}
+
+// Verify that ByteSink is subclassable and Flush() overridable.
+class FlushingByteSink : public StringByteSink {
+ public:
+ explicit FlushingByteSink(string* dest) : StringByteSink(dest) {}
+ virtual void Flush() { Append("z", 1); }
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FlushingByteSink);
+};
+
+// Write and Flush via the ByteSink superclass interface.
+void WriteAndFlush(ByteSink* s) {
+ s->Append("abc", 3);
+ s->Flush();
+}
+
+TEST(ByteSinkTest, Flush) {
+ string str;
+ FlushingByteSink f_sink(&str);
+ WriteAndFlush(&f_sink);
+ EXPECT_STREQ("abcz", str.c_str());
+}
+
+} // namespace
+} // namespace strings
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/casts.h b/src/google/protobuf/stubs/casts.h
index cccf65a1..be652849 100644
--- a/src/google/protobuf/stubs/casts.h
+++ b/src/google/protobuf/stubs/casts.h
@@ -111,12 +111,22 @@ inline To down_cast(From& f) {
return *static_cast<ToAsPointer>(&f);
}
+template<typename To, typename From>
+inline To bit_cast(const From& from) {
+ GOOGLE_COMPILE_ASSERT(sizeof(From) == sizeof(To),
+ bit_cast_with_different_sizes);
+ To dest;
+ memcpy(&dest, &from, sizeof(dest));
+ return dest;
+}
+
} // namespace internal
// We made these internal so that they would show up as such in the docs,
// but we don't want to stick "internal::" in front of them everywhere.
using internal::implicit_cast;
using internal::down_cast;
+using internal::bit_cast;
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/common.cc b/src/google/protobuf/stubs/common.cc
index 22cc5aa1..d470fc72 100644
--- a/src/google/protobuf/stubs/common.cc
+++ b/src/google/protobuf/stubs/common.cc
@@ -32,6 +32,9 @@
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/strutil.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
@@ -146,6 +149,27 @@ LogMessage& LogMessage::operator<<(const char* value) {
return *this;
}
+LogMessage& LogMessage::operator<<(const StringPiece& value) {
+ message_ += value.ToString();
+ return *this;
+}
+
+LogMessage& LogMessage::operator<<(long long value) {
+ message_ += SimpleItoa(value);
+ return *this;
+}
+
+LogMessage& LogMessage::operator<<(unsigned long long value) {
+ message_ += SimpleItoa(value);
+ return *this;
+}
+
+LogMessage& LogMessage::operator<<(
+ const ::google::protobuf::util::Status& status) {
+ message_ += status.ToString();
+ return *this;
+}
+
// Since this is just for logging, we don't care if the current locale changes
// the results -- in fact, we probably prefer that. So we use snprintf()
// instead of Simple*toa().
@@ -165,7 +189,7 @@ LogMessage& LogMessage::operator<<(const char* value) {
DECLARE_STREAM_OPERATOR(char , "%c" )
DECLARE_STREAM_OPERATOR(int , "%d" )
-DECLARE_STREAM_OPERATOR(uint , "%u" )
+DECLARE_STREAM_OPERATOR(unsigned int , "%u" )
DECLARE_STREAM_OPERATOR(long , "%ld")
DECLARE_STREAM_OPERATOR(unsigned long, "%lu")
DECLARE_STREAM_OPERATOR(double , "%g" )
diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h
index 49d16020..c3620146 100644
--- a/src/google/protobuf/stubs/common.h
+++ b/src/google/protobuf/stubs/common.h
@@ -48,6 +48,26 @@
#include <stdint.h>
#endif
+#undef PROTOBUF_LITTLE_ENDIAN
+#ifdef _MSC_VER
+ // Assuming windows is always little-endian.
+ #if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+ #define PROTOBUF_LITTLE_ENDIAN 1
+ #endif
+ #if _MSC_VER >= 1300
+ // If MSVC has "/RTCc" set, it will complain about truncating casts at
+ // runtime. This file contains some intentional truncating casts.
+ #pragma runtime_checks("c", off)
+ #endif
+#else
+ #include <sys/param.h> // __BYTE_ORDER
+ #if ((defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) || \
+ (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN)) && \
+ !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+ #define PROTOBUF_LITTLE_ENDIAN 1
+ #endif
+#endif
+
#ifndef PROTOBUF_USE_EXCEPTIONS
#if defined(_MSC_VER) && defined(_CPPUNWIND)
#define PROTOBUF_USE_EXCEPTIONS 1
@@ -100,6 +120,12 @@ namespace protobuf {
TypeName(const TypeName&); \
void operator=(const TypeName&)
+#undef GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS
+#define GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
#if defined(_MSC_VER) && defined(PROTOBUF_USE_DLLS)
#ifdef LIBPROTOBUF_EXPORTS
#define LIBPROTOBUF_EXPORT __declspec(dllexport)
@@ -660,6 +686,10 @@ enum LogLevel {
#endif
};
+class StringPiece;
+namespace util {
+class Status;
+}
namespace internal {
class LogFinisher;
@@ -673,11 +703,15 @@ class LIBPROTOBUF_EXPORT LogMessage {
LogMessage& operator<<(const char* value);
LogMessage& operator<<(char value);
LogMessage& operator<<(int value);
- LogMessage& operator<<(uint value);
+ LogMessage& operator<<(unsigned int value);
LogMessage& operator<<(long value);
LogMessage& operator<<(unsigned long value);
+ LogMessage& operator<<(long long value);
+ LogMessage& operator<<(unsigned long long value);
LogMessage& operator<<(double value);
LogMessage& operator<<(void* value);
+ LogMessage& operator<<(const StringPiece& value);
+ LogMessage& operator<<(const ::google::protobuf::util::Status& status);
private:
friend class LogFinisher;
@@ -696,6 +730,11 @@ class LIBPROTOBUF_EXPORT LogFinisher {
void operator=(LogMessage& other);
};
+template<typename T>
+bool IsOk(T status) { return status.ok(); }
+template<>
+inline bool IsOk(bool status) { return status; }
+
} // namespace internal
// Undef everything in case we're being mixed with some other Google library
@@ -717,6 +756,7 @@ class LIBPROTOBUF_EXPORT LogFinisher {
#undef GOOGLE_DLOG
#undef GOOGLE_DCHECK
+#undef GOOGLE_DCHECK_OK
#undef GOOGLE_DCHECK_EQ
#undef GOOGLE_DCHECK_NE
#undef GOOGLE_DCHECK_LT
@@ -733,7 +773,7 @@ class LIBPROTOBUF_EXPORT LogFinisher {
#define GOOGLE_CHECK(EXPRESSION) \
GOOGLE_LOG_IF(FATAL, !(EXPRESSION)) << "CHECK failed: " #EXPRESSION ": "
-#define GOOGLE_CHECK_OK(A) GOOGLE_CHECK(A)
+#define GOOGLE_CHECK_OK(A) GOOGLE_CHECK(::google::protobuf::internal::IsOk(A))
#define GOOGLE_CHECK_EQ(A, B) GOOGLE_CHECK((A) == (B))
#define GOOGLE_CHECK_NE(A, B) GOOGLE_CHECK((A) != (B))
#define GOOGLE_CHECK_LT(A, B) GOOGLE_CHECK((A) < (B))
@@ -760,6 +800,7 @@ T* CheckNotNull(const char* /* file */, int /* line */,
#define GOOGLE_DLOG GOOGLE_LOG_IF(INFO, false)
#define GOOGLE_DCHECK(EXPRESSION) while(false) GOOGLE_CHECK(EXPRESSION)
+#define GOOGLE_DCHECK_OK(E) GOOGLE_DCHECK(::google::protobuf::internal::IsOk(E))
#define GOOGLE_DCHECK_EQ(A, B) GOOGLE_DCHECK((A) == (B))
#define GOOGLE_DCHECK_NE(A, B) GOOGLE_DCHECK((A) != (B))
#define GOOGLE_DCHECK_LT(A, B) GOOGLE_DCHECK((A) < (B))
@@ -772,6 +813,7 @@ T* CheckNotNull(const char* /* file */, int /* line */,
#define GOOGLE_DLOG GOOGLE_LOG
#define GOOGLE_DCHECK GOOGLE_CHECK
+#define GOOGLE_DCHECK_OK GOOGLE_CHECK_OK
#define GOOGLE_DCHECK_EQ GOOGLE_CHECK_EQ
#define GOOGLE_DCHECK_NE GOOGLE_CHECK_NE
#define GOOGLE_DCHECK_LT GOOGLE_CHECK_LT
@@ -883,6 +925,30 @@ class LIBPROTOBUF_EXPORT Closure {
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Closure);
};
+template<typename R, typename A1>
+class LIBPROTOBUF_EXPORT ResultCallback1 {
+ public:
+ ResultCallback1() {}
+ virtual ~ResultCallback1() {}
+
+ virtual R Run(A1) = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback1);
+};
+
+template<typename R, typename A1, typename A2>
+class LIBPROTOBUF_EXPORT ResultCallback2 {
+ public:
+ ResultCallback2() {}
+ virtual ~ResultCallback2() {}
+
+ virtual R Run(A1,A2) = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback2);
+};
+
namespace internal {
class LIBPROTOBUF_EXPORT FunctionClosure0 : public Closure {
@@ -1021,6 +1087,96 @@ class MethodClosure2 : public Closure {
Arg2 arg2_;
};
+template<typename R, typename Arg1>
+class FunctionResultCallback_0_1 : public ResultCallback1<R, Arg1> {
+ public:
+ typedef R (*FunctionType)(Arg1 arg1);
+
+ FunctionResultCallback_0_1(FunctionType function, bool self_deleting)
+ : function_(function), self_deleting_(self_deleting) {}
+ ~FunctionResultCallback_0_1() {}
+
+ R Run(Arg1 a1) {
+ bool needs_delete = self_deleting_; // read in case callback deletes
+ R result = function_(a1);
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ FunctionType function_;
+ bool self_deleting_;
+};
+
+template<typename R, typename P1, typename A1>
+class FunctionResultCallback_1_1 : public ResultCallback1<R, A1> {
+ public:
+ typedef R (*FunctionType)(P1, A1);
+
+ FunctionResultCallback_1_1(FunctionType function, bool self_deleting,
+ P1 p1)
+ : function_(function), self_deleting_(self_deleting), p1_(p1) {}
+ ~FunctionResultCallback_1_1() {}
+
+ R Run(A1 a1) {
+ bool needs_delete = self_deleting_; // read in case callback deletes
+ R result = function_(p1_, a1);
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ FunctionType function_;
+ bool self_deleting_;
+ P1 p1_;
+};
+
+// Duplicate this again in the type_traits.h, due to dependency problems.
+template <class T> struct internal_remove_reference;
+template<typename T> struct internal_remove_reference { typedef T type; };
+template<typename T> struct internal_remove_reference<T&> { typedef T type; };
+
+template <typename T>
+struct InternalConstRef {
+ typedef typename internal_remove_reference<T>::type base_type;
+ typedef const base_type& type;
+};
+
+template <typename R, typename T, typename P1, typename P2, typename P3,
+ typename P4, typename P5, typename A1, typename A2>
+class MethodResultCallback_5_2 : public ResultCallback2<R, A1, A2> {
+ public:
+ typedef R (T::*MethodType)(P1, P2, P3, P4, P5, A1, A2);
+ MethodResultCallback_5_2(T* object, MethodType method, bool self_deleting,
+ P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)
+ : object_(object),
+ method_(method),
+ self_deleting_(self_deleting),
+ p1_(p1),
+ p2_(p2),
+ p3_(p3),
+ p4_(p4),
+ p5_(p5) {}
+ ~MethodResultCallback_5_2() {}
+
+ R Run(A1 a1, A2 a2) {
+ bool needs_delete = self_deleting_;
+ R result = (object_->*method_)(p1_, p2_, p3_, p4_, p5_, a1, a2);
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ T* object_;
+ MethodType method_;
+ bool self_deleting_;
+ typename internal_remove_reference<P1>::type p1_;
+ typename internal_remove_reference<P2>::type p2_;
+ typename internal_remove_reference<P3>::type p3_;
+ typename internal_remove_reference<P4>::type p4_;
+ typename internal_remove_reference<P5>::type p5_;
+};
+
} // namespace internal
// See Closure.
@@ -1106,6 +1262,48 @@ inline Closure* NewPermanentCallback(
object, method, false, arg1, arg2);
}
+// See ResultCallback1
+template<typename R, typename A1>
+inline ResultCallback1<R, A1>* NewCallback(R (*function)(A1)) {
+ return new internal::FunctionResultCallback_0_1<R, A1>(function, true);
+}
+
+// See ResultCallback1
+template<typename R, typename A1>
+inline ResultCallback1<R, A1>* NewPermanentCallback(R (*function)(A1)) {
+ return new internal::FunctionResultCallback_0_1<R, A1>(function, false);
+}
+
+// See ResultCallback1
+template<typename R, typename P1, typename A1>
+inline ResultCallback1<R, A1>* NewCallback(R (*function)(P1, A1), P1 p1) {
+ return new internal::FunctionResultCallback_1_1<R, P1, A1>(
+ function, true, p1);
+}
+
+// See ResultCallback1
+template<typename R, typename P1, typename A1>
+inline ResultCallback1<R, A1>* NewPermanentCallback(
+ R (*function)(P1, A1), P1 p1) {
+ return new internal::FunctionResultCallback_1_1<R, P1, A1>(
+ function, false, p1);
+}
+
+// See MethodResultCallback_5_2
+template <typename R, typename T, typename P1, typename P2, typename P3,
+ typename P4, typename P5, typename A1, typename A2>
+inline ResultCallback2<R, A1, A2>* NewPermanentCallback(
+ T* object, R (T::*function)(P1, P2, P3, P4, P5, A1, A2),
+ typename internal::InternalConstRef<P1>::type p1,
+ typename internal::InternalConstRef<P2>::type p2,
+ typename internal::InternalConstRef<P3>::type p3,
+ typename internal::InternalConstRef<P4>::type p4,
+ typename internal::InternalConstRef<P5>::type p5) {
+ return new internal::MethodResultCallback_5_2<R, T, P1, P2, P3, P4, P5, A1,
+ A2>(object, function, false, p1,
+ p2, p3, p4, p5);
+}
+
// A function which does nothing. Useful for creating no-op callbacks, e.g.:
// Closure* nothing = NewCallback(&DoNothing);
void LIBPROTOBUF_EXPORT DoNothing();
@@ -1226,9 +1424,114 @@ LIBPROTOBUF_EXPORT bool IsStructurallyValidUTF8(const char* buf, int len);
} // namespace internal
// ===================================================================
+// from google3/base/port.h
+
+// The following guarantees declaration of the byte swap functions, and
+// defines __BYTE_ORDER for MSVC
+#ifdef _MSC_VER
+#include <stdlib.h> // NOLINT(build/include)
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#define bswap_16(x) _byteswap_ushort(x)
+#define bswap_32(x) _byteswap_ulong(x)
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+// Mac OS X / Darwin features
+#include <libkern/OSByteOrder.h>
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#elif defined(__GLIBC__) || defined(__CYGWIN__)
+#include <byteswap.h> // IWYU pragma: export
+
+#else
+
+static inline uint16 bswap_16(uint16 x) {
+ return static_cast<uint16>(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8));
+}
+#define bswap_16(x) bswap_16(x)
+static inline uint32 bswap_32(uint32 x) {
+ return (((x & 0xFF) << 24) |
+ ((x & 0xFF00) << 8) |
+ ((x & 0xFF0000) >> 8) |
+ ((x & 0xFF000000) >> 24));
+}
+#define bswap_32(x) bswap_32(x)
+static inline uint64 bswap_64(uint64 x) {
+ return (((x & GG_ULONGLONG(0xFF)) << 56) |
+ ((x & GG_ULONGLONG(0xFF00)) << 40) |
+ ((x & GG_ULONGLONG(0xFF0000)) << 24) |
+ ((x & GG_ULONGLONG(0xFF000000)) << 8) |
+ ((x & GG_ULONGLONG(0xFF00000000)) >> 8) |
+ ((x & GG_ULONGLONG(0xFF0000000000)) >> 24) |
+ ((x & GG_ULONGLONG(0xFF000000000000)) >> 40) |
+ ((x & GG_ULONGLONG(0xFF00000000000000)) >> 56));
+}
+#define bswap_64(x) bswap_64(x)
+
+#endif
+
+// ===================================================================
// from google3/util/endian/endian.h
LIBPROTOBUF_EXPORT uint32 ghtonl(uint32 x);
+class BigEndian {
+ public:
+#ifdef PROTOBUF_LITTLE_ENDIAN
+
+ static uint16 FromHost16(uint16 x) { return bswap_16(x); }
+ static uint16 ToHost16(uint16 x) { return bswap_16(x); }
+
+ static uint32 FromHost32(uint32 x) { return bswap_32(x); }
+ static uint32 ToHost32(uint32 x) { return bswap_32(x); }
+
+ static uint64 FromHost64(uint64 x) { return bswap_64(x); }
+ static uint64 ToHost64(uint64 x) { return bswap_64(x); }
+
+ static bool IsLittleEndian() { return true; }
+
+#else
+
+ static uint16 FromHost16(uint16 x) { return x; }
+ static uint16 ToHost16(uint16 x) { return x; }
+
+ static uint32 FromHost32(uint32 x) { return x; }
+ static uint32 ToHost32(uint32 x) { return x; }
+
+ static uint64 FromHost64(uint64 x) { return x; }
+ static uint64 ToHost64(uint64 x) { return x; }
+
+ static bool IsLittleEndian() { return false; }
+
+#endif /* ENDIAN */
+
+ // Functions to do unaligned loads and stores in big-endian order.
+ static uint16 Load16(const void *p) {
+ return ToHost16(GOOGLE_UNALIGNED_LOAD16(p));
+ }
+
+ static void Store16(void *p, uint16 v) {
+ GOOGLE_UNALIGNED_STORE16(p, FromHost16(v));
+ }
+
+ static uint32 Load32(const void *p) {
+ return ToHost32(GOOGLE_UNALIGNED_LOAD32(p));
+ }
+
+ static void Store32(void *p, uint32 v) {
+ GOOGLE_UNALIGNED_STORE32(p, FromHost32(v));
+ }
+
+ static uint64 Load64(const void *p) {
+ return ToHost64(GOOGLE_UNALIGNED_LOAD64(p));
+ }
+
+ static void Store64(void *p, uint64 v) {
+ GOOGLE_UNALIGNED_STORE64(p, FromHost64(v));
+ }
+};
+
// ===================================================================
// Shutdown support.
diff --git a/src/google/protobuf/stubs/mathlimits.cc b/src/google/protobuf/stubs/mathlimits.cc
new file mode 100644
index 00000000..0373b2bb
--- /dev/null
+++ b/src/google/protobuf/stubs/mathlimits.cc
@@ -0,0 +1,144 @@
+// 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.
+
+// All Rights Reserved.
+//
+// Author: Maxim Lifantsev
+//
+
+#include <google/protobuf/stubs/mathlimits.h>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+
+// MSVC++ 2005 and older compilers think the header declaration was a
+// definition, and erroneously flag these as a duplicate definition.
+#if defined(COMPILER_MSVC) || __cpluscplus < 201103L
+
+#define DEF_COMMON_LIMITS(Type)
+#define DEF_UNSIGNED_INT_LIMITS(Type)
+#define DEF_SIGNED_INT_LIMITS(Type)
+#define DEF_PRECISION_LIMITS(Type)
+
+#else
+
+#define DEF_COMMON_LIMITS(Type) \
+const bool MathLimits<Type>::kIsSigned; \
+const bool MathLimits<Type>::kIsInteger; \
+const int MathLimits<Type>::kMin10Exp; \
+const int MathLimits<Type>::kMax10Exp;
+
+#define DEF_UNSIGNED_INT_LIMITS(Type) \
+DEF_COMMON_LIMITS(Type) \
+const Type MathLimits<Type>::kPosMin; \
+const Type MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kMin; \
+const Type MathLimits<Type>::kMax; \
+const Type MathLimits<Type>::kEpsilon; \
+const Type MathLimits<Type>::kStdError;
+
+#define DEF_SIGNED_INT_LIMITS(Type) \
+DEF_UNSIGNED_INT_LIMITS(Type) \
+const Type MathLimits<Type>::kNegMin; \
+const Type MathLimits<Type>::kNegMax;
+
+#define DEF_PRECISION_LIMITS(Type) \
+const int MathLimits<Type>::kPrecisionDigits;
+
+#endif // not COMPILER_MSVC
+
+// http://en.wikipedia.org/wiki/Quadruple_precision_floating-point_format#Double-double_arithmetic
+// With some compilers (gcc 4.6.x) on some platforms (powerpc64),
+// "long double" is implemented as a pair of double: "double double" format.
+// This causes a problem with epsilon (eps).
+// eps is the smallest positive number such that 1.0 + eps > 1.0
+//
+// Normal format: 1.0 + e = 1.0...01 // N-1 zeros for N fraction bits
+// D-D format: 1.0 + e = 1.000...0001 // epsilon can be very small
+//
+// In the normal format, 1.0 + e has to fit in one stretch of bits.
+// The maximum rounding error is half of eps.
+//
+// In the double-double format, 1.0 + e splits across two doubles:
+// 1.0 in the high double, e in the low double, and they do not have to
+// be contiguous. The maximum rounding error on a value close to 1.0 is
+// much larger than eps.
+//
+// Some code checks for errors by comparing a computed value to a golden
+// value +/- some multiple of the maximum rounding error. The maximum
+// rounding error is not available so we use eps as an approximation
+// instead. That fails when long double is in the double-double format.
+// Therefore, we define kStdError as a multiple of
+// max(DBL_EPSILON * DBL_EPSILON, kEpsilon) rather than a multiple of kEpsilon.
+
+#define DEF_FP_LIMITS(Type, PREFIX) \
+DEF_COMMON_LIMITS(Type) \
+const Type MathLimits<Type>::kPosMin = PREFIX##_MIN; \
+const Type MathLimits<Type>::kPosMax = PREFIX##_MAX; \
+const Type MathLimits<Type>::kMin = -MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kMax = MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kNegMin = -MathLimits<Type>::kPosMin; \
+const Type MathLimits<Type>::kNegMax = -MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kEpsilon = PREFIX##_EPSILON; \
+/* 32 is 5 bits of mantissa error; should be adequate for common errors */ \
+const Type MathLimits<Type>::kStdError = \
+ 32 * (DBL_EPSILON * DBL_EPSILON > MathLimits<Type>::kEpsilon \
+ ? DBL_EPSILON * DBL_EPSILON : MathLimits<Type>::kEpsilon); \
+DEF_PRECISION_LIMITS(Type) \
+const Type MathLimits<Type>::kNaN = HUGE_VAL - HUGE_VAL; \
+const Type MathLimits<Type>::kPosInf = HUGE_VAL; \
+const Type MathLimits<Type>::kNegInf = -HUGE_VAL;
+
+// The following are *not* casts!
+DEF_SIGNED_INT_LIMITS(int8)
+DEF_SIGNED_INT_LIMITS(int16) // NOLINT(readability/casting)
+DEF_SIGNED_INT_LIMITS(int32) // NOLINT(readability/casting)
+DEF_SIGNED_INT_LIMITS(int64) // NOLINT(readability/casting)
+DEF_UNSIGNED_INT_LIMITS(uint8)
+DEF_UNSIGNED_INT_LIMITS(uint16) // NOLINT(readability/casting)
+DEF_UNSIGNED_INT_LIMITS(uint32) // NOLINT(readability/casting)
+DEF_UNSIGNED_INT_LIMITS(uint64) // NOLINT(readability/casting)
+
+DEF_SIGNED_INT_LIMITS(long int)
+DEF_UNSIGNED_INT_LIMITS(unsigned long int)
+
+DEF_FP_LIMITS(float, FLT)
+DEF_FP_LIMITS(double, DBL)
+DEF_FP_LIMITS(long double, LDBL);
+
+#undef DEF_COMMON_LIMITS
+#undef DEF_SIGNED_INT_LIMITS
+#undef DEF_UNSIGNED_INT_LIMITS
+#undef DEF_FP_LIMITS
+#undef DEF_PRECISION_LIMITS
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/mathlimits.h b/src/google/protobuf/stubs/mathlimits.h
new file mode 100644
index 00000000..d9846940
--- /dev/null
+++ b/src/google/protobuf/stubs/mathlimits.h
@@ -0,0 +1,279 @@
+// 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.
+
+// All Rights Reserved.
+//
+// Author: Maxim Lifantsev
+//
+// Useful integer and floating point limits and type traits.
+//
+// This partially replaces/duplictes numeric_limits<> from <limits>.
+// We get a Google-style class that we have a greater control over
+// and thus can add new features to it or fix whatever happens to be broken in
+// numeric_limits for the compilers we use.
+//
+
+#ifndef UTIL_MATH_MATHLIMITS_H__
+#define UTIL_MATH_MATHLIMITS_H__
+
+// <math.h> lacks a lot of prototypes. However, this file needs <math.h> to
+// access old-fashioned isinf et al. Even worse more: this file must not
+// include <cmath> because that breaks the definition of isinf with gcc 4.9.
+//
+// TODO(mec): after C++11 everywhere, use <cmath> and std::isinf in this file.
+#include <math.h>
+#include <string.h>
+
+#include <cfloat>
+
+#include <google/protobuf/stubs/common.h>
+
+// ========================================================================= //
+
+// Useful integer and floating point limits and type traits.
+// This is just for the documentation;
+// real members are defined in our specializations below.
+namespace google {
+namespace protobuf {
+template<typename T> struct MathLimits {
+ // Type name.
+ typedef T Type;
+ // Unsigned version of the Type with the same byte size.
+ // Same as Type for floating point and unsigned types.
+ typedef T UnsignedType;
+ // If the type supports negative values.
+ static const bool kIsSigned;
+ // If the type supports only integer values.
+ static const bool kIsInteger;
+ // Magnitude-wise smallest representable positive value.
+ static const Type kPosMin;
+ // Magnitude-wise largest representable positive value.
+ static const Type kPosMax;
+ // Smallest representable value.
+ static const Type kMin;
+ // Largest representable value.
+ static const Type kMax;
+ // Magnitude-wise smallest representable negative value.
+ // Present only if kIsSigned.
+ static const Type kNegMin;
+ // Magnitude-wise largest representable negative value.
+ // Present only if kIsSigned.
+ static const Type kNegMax;
+ // Smallest integer x such that 10^x is representable.
+ static const int kMin10Exp;
+ // Largest integer x such that 10^x is representable.
+ static const int kMax10Exp;
+ // Smallest positive value such that Type(1) + kEpsilon != Type(1)
+ static const Type kEpsilon;
+ // Typical rounding error that is enough to cover
+ // a few simple floating-point operations.
+ // Slightly larger than kEpsilon to account for a few rounding errors.
+ // Is zero if kIsInteger.
+ static const Type kStdError;
+ // Number of decimal digits of mantissa precision.
+ // Present only if !kIsInteger.
+ static const int kPrecisionDigits;
+ // Not a number, i.e. result of 0/0.
+ // Present only if !kIsInteger.
+ static const Type kNaN;
+ // Positive infinity, i.e. result of 1/0.
+ // Present only if !kIsInteger.
+ static const Type kPosInf;
+ // Negative infinity, i.e. result of -1/0.
+ // Present only if !kIsInteger.
+ static const Type kNegInf;
+
+ // NOTE: Special floating point values behave
+ // in a special (but mathematically-logical) way
+ // in terms of (in)equalty comparison and mathematical operations
+ // -- see out unittest for examples.
+
+ // Special floating point value testers.
+ // Present in integer types for convenience.
+ static bool IsFinite(const Type x);
+ static bool IsNaN(const Type x);
+ static bool IsInf(const Type x);
+ static bool IsPosInf(const Type x);
+ static bool IsNegInf(const Type x);
+};
+
+// ========================================================================= //
+
+// All #define-s below are simply to refactor the declarations of
+// MathLimits template specializations.
+// They are all #undef-ined below.
+
+// The hoop-jumping in *_INT_(MAX|MIN) below is so that the compiler does not
+// get an overflow while computing the constants.
+
+#define SIGNED_INT_MAX(Type) \
+ (((Type(1) << (sizeof(Type)*8 - 2)) - 1) + (Type(1) << (sizeof(Type)*8 - 2)))
+
+#define SIGNED_INT_MIN(Type) \
+ (-(Type(1) << (sizeof(Type)*8 - 2)) - (Type(1) << (sizeof(Type)*8 - 2)))
+
+#define UNSIGNED_INT_MAX(Type) \
+ (((Type(1) << (sizeof(Type)*8 - 1)) - 1) + (Type(1) << (sizeof(Type)*8 - 1)))
+
+// Compile-time selected log10-related constants for integer types.
+#define SIGNED_MAX_10_EXP(Type) \
+ (sizeof(Type) == 1 ? 2 : ( \
+ sizeof(Type) == 2 ? 4 : ( \
+ sizeof(Type) == 4 ? 9 : ( \
+ sizeof(Type) == 8 ? 18 : -1))))
+
+#define UNSIGNED_MAX_10_EXP(Type) \
+ (sizeof(Type) == 1 ? 2 : ( \
+ sizeof(Type) == 2 ? 4 : ( \
+ sizeof(Type) == 4 ? 9 : ( \
+ sizeof(Type) == 8 ? 19 : -1))))
+
+#define DECL_INT_LIMIT_FUNCS \
+ static bool IsFinite(const Type /*x*/) { return true; } \
+ static bool IsNaN(const Type /*x*/) { return false; } \
+ static bool IsInf(const Type /*x*/) { return false; } \
+ static bool IsPosInf(const Type /*x*/) { return false; } \
+ static bool IsNegInf(const Type /*x*/) { return false; }
+
+#define DECL_SIGNED_INT_LIMITS(IntType, UnsignedIntType) \
+template<> \
+struct LIBPROTOBUF_EXPORT MathLimits<IntType> { \
+ typedef IntType Type; \
+ typedef UnsignedIntType UnsignedType; \
+ static const bool kIsSigned = true; \
+ static const bool kIsInteger = true; \
+ static const Type kPosMin = 1; \
+ static const Type kPosMax = SIGNED_INT_MAX(Type); \
+ static const Type kMin = SIGNED_INT_MIN(Type); \
+ static const Type kMax = kPosMax; \
+ static const Type kNegMin = -1; \
+ static const Type kNegMax = kMin; \
+ static const int kMin10Exp = 0; \
+ static const int kMax10Exp = SIGNED_MAX_10_EXP(Type); \
+ static const Type kEpsilon = 1; \
+ static const Type kStdError = 0; \
+ DECL_INT_LIMIT_FUNCS \
+};
+
+#define DECL_UNSIGNED_INT_LIMITS(IntType) \
+template<> \
+struct LIBPROTOBUF_EXPORT MathLimits<IntType> { \
+ typedef IntType Type; \
+ typedef IntType UnsignedType; \
+ static const bool kIsSigned = false; \
+ static const bool kIsInteger = true; \
+ static const Type kPosMin = 1; \
+ static const Type kPosMax = UNSIGNED_INT_MAX(Type); \
+ static const Type kMin = 0; \
+ static const Type kMax = kPosMax; \
+ static const int kMin10Exp = 0; \
+ static const int kMax10Exp = UNSIGNED_MAX_10_EXP(Type); \
+ static const Type kEpsilon = 1; \
+ static const Type kStdError = 0; \
+ DECL_INT_LIMIT_FUNCS \
+};
+
+DECL_SIGNED_INT_LIMITS(signed char, unsigned char)
+DECL_SIGNED_INT_LIMITS(signed short int, unsigned short int)
+DECL_SIGNED_INT_LIMITS(signed int, unsigned int)
+DECL_SIGNED_INT_LIMITS(signed long int, unsigned long int)
+DECL_SIGNED_INT_LIMITS(signed long long int, unsigned long long int)
+DECL_UNSIGNED_INT_LIMITS(unsigned char)
+DECL_UNSIGNED_INT_LIMITS(unsigned short int)
+DECL_UNSIGNED_INT_LIMITS(unsigned int)
+DECL_UNSIGNED_INT_LIMITS(unsigned long int)
+DECL_UNSIGNED_INT_LIMITS(unsigned long long int)
+
+#undef DECL_SIGNED_INT_LIMITS
+#undef DECL_UNSIGNED_INT_LIMITS
+#undef SIGNED_INT_MAX
+#undef SIGNED_INT_MIN
+#undef UNSIGNED_INT_MAX
+#undef SIGNED_MAX_10_EXP
+#undef UNSIGNED_MAX_10_EXP
+#undef DECL_INT_LIMIT_FUNCS
+
+// ========================================================================= //
+#ifdef WIN32 // Lacks built-in isnan() and isinf()
+#define DECL_FP_LIMIT_FUNCS \
+ static bool IsFinite(const Type x) { return _finite(x); } \
+ static bool IsNaN(const Type x) { return _isnan(x); } \
+ static bool IsInf(const Type x) { return (_fpclass(x) & (_FPCLASS_NINF | _FPCLASS_PINF)) != 0; } \
+ static bool IsPosInf(const Type x) { return _fpclass(x) == _FPCLASS_PINF; } \
+ static bool IsNegInf(const Type x) { return _fpclass(x) == _FPCLASS_NINF; }
+#else
+#define DECL_FP_LIMIT_FUNCS \
+ static bool IsFinite(const Type x) { return !isinf(x) && !isnan(x); } \
+ static bool IsNaN(const Type x) { return isnan(x); } \
+ static bool IsInf(const Type x) { return isinf(x); } \
+ static bool IsPosInf(const Type x) { return isinf(x) && x > 0; } \
+ static bool IsNegInf(const Type x) { return isinf(x) && x < 0; }
+#endif
+
+// We can't put floating-point constant values in the header here because
+// such constants are not considered to be primitive-type constants by gcc.
+// CAVEAT: Hence, they are going to be initialized only during
+// the global objects construction time.
+#define DECL_FP_LIMITS(FP_Type, PREFIX) \
+template<> \
+struct LIBPROTOBUF_EXPORT MathLimits<FP_Type> { \
+ typedef FP_Type Type; \
+ typedef FP_Type UnsignedType; \
+ static const bool kIsSigned = true; \
+ static const bool kIsInteger = false; \
+ static const Type kPosMin; \
+ static const Type kPosMax; \
+ static const Type kMin; \
+ static const Type kMax; \
+ static const Type kNegMin; \
+ static const Type kNegMax; \
+ static const int kMin10Exp = PREFIX##_MIN_10_EXP; \
+ static const int kMax10Exp = PREFIX##_MAX_10_EXP; \
+ static const Type kEpsilon; \
+ static const Type kStdError; \
+ static const int kPrecisionDigits = PREFIX##_DIG; \
+ static const Type kNaN; \
+ static const Type kPosInf; \
+ static const Type kNegInf; \
+ DECL_FP_LIMIT_FUNCS \
+};
+
+DECL_FP_LIMITS(float, FLT)
+DECL_FP_LIMITS(double, DBL)
+DECL_FP_LIMITS(long double, LDBL)
+
+#undef DECL_FP_LIMITS
+#undef DECL_FP_LIMIT_FUNCS
+
+// ========================================================================= //
+} // namespace protobuf
+} // namespace google
+
+#endif // UTIL_MATH_MATHLIMITS_H__
diff --git a/src/google/protobuf/stubs/mathutil.h b/src/google/protobuf/stubs/mathutil.h
new file mode 100644
index 00000000..87ca5e91
--- /dev/null
+++ b/src/google/protobuf/stubs/mathutil.h
@@ -0,0 +1,149 @@
+// 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 GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
+#define GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
+
+#include <float.h>
+#include <math.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/mathlimits.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+template<typename T>
+bool IsNan(T value) {
+ return false;
+}
+template<>
+inline bool IsNan(float value) { return isnan(value); }
+template<>
+inline bool IsNan(double value) { return isnan(value); }
+
+template<typename T>
+bool AlmostEquals(T a, T b) {
+ return a == b;
+}
+template<>
+inline bool AlmostEquals(float a, float b) {
+ return fabs(a - b) < 32 * FLT_EPSILON;
+}
+
+template<>
+inline bool AlmostEquals(double a, double b) {
+ return fabs(a - b) < 32 * DBL_EPSILON;
+}
+} // namespace internal
+
+class MathUtil {
+ public:
+ template<typename T>
+ static T Sign(T value) {
+ if (value == T(0) || ::google::protobuf::internal::IsNan<T>(value)) {
+ return value;
+ }
+ return value > T(0) ? value : -value;
+ }
+
+ template<typename T>
+ static bool AlmostEquals(T a, T b) {
+ return ::google::protobuf::internal::AlmostEquals(a, b);
+ }
+
+ // Largest of two values.
+ // Works correctly for special floating point values.
+ // Note: 0.0 and -0.0 are not differentiated by Max (Max(0.0, -0.0) is -0.0),
+ // which should be OK because, although they (can) have different
+ // bit representation, they are observably the same when examined
+ // with arithmetic and (in)equality operators.
+ template<typename T>
+ static T Max(const T x, const T y) {
+ return MathLimits<T>::IsNaN(x) || x > y ? x : y;
+ }
+
+ // Absolute value of x
+ // Works correctly for unsigned types and
+ // for special floating point values.
+ // Note: 0.0 and -0.0 are not differentiated by Abs (Abs(0.0) is -0.0),
+ // which should be OK: see the comment for Max above.
+ template<typename T>
+ static T Abs(const T x) {
+ return x > T(0) ? x : -x;
+ }
+
+ // Absolute value of the difference between two numbers.
+ // Works correctly for signed types and special floating point values.
+ template<typename T>
+ static typename MathLimits<T>::UnsignedType AbsDiff(const T x, const T y) {
+ // Carries out arithmetic as unsigned to avoid overflow.
+ typedef typename MathLimits<T>::UnsignedType R;
+ return x > y ? R(x) - R(y) : R(y) - R(x);
+ }
+
+ // If two (usually floating point) numbers are within a certain
+ // fraction of their magnitude or within a certain absolute margin of error.
+ // This is the same as the following but faster:
+ // WithinFraction(x, y, fraction) || WithinMargin(x, y, margin)
+ // E.g. WithinFraction(0.0, 1e-10, 1e-5) is false but
+ // WithinFractionOrMargin(0.0, 1e-10, 1e-5, 1e-5) is true.
+ template<typename T>
+ static bool WithinFractionOrMargin(const T x, const T y,
+ const T fraction, const T margin);
+};
+
+template<typename T>
+bool MathUtil::WithinFractionOrMargin(const T x, const T y,
+ const T fraction, const T margin) {
+ // Not just "0 <= fraction" to fool the compiler for unsigned types.
+ GOOGLE_DCHECK((T(0) < fraction || T(0) == fraction) &&
+ fraction < T(1) &&
+ margin >= T(0));
+
+ // Template specialization will convert the if() condition to a constant,
+ // which will cause the compiler to generate code for either the "if" part
+ // or the "then" part. In this way we avoid a compiler warning
+ // about a potential integer overflow in crosstool v12 (gcc 4.3.1).
+ if (MathLimits<T>::kIsInteger) {
+ return x == y;
+ } else {
+ // IsFinite checks are to make kPosInf and kNegInf not within fraction
+ if (!MathLimits<T>::IsFinite(x) && !MathLimits<T>::IsFinite(y)) {
+ return false;
+ }
+ T relative_margin = static_cast<T>(fraction * Max(Abs(x), Abs(y)));
+ return AbsDiff(x, y) <= Max(margin, relative_margin);
+ }
+}
+
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
diff --git a/src/google/protobuf/stubs/status.cc b/src/google/protobuf/stubs/status.cc
new file mode 100644
index 00000000..7314c563
--- /dev/null
+++ b/src/google/protobuf/stubs/status.cc
@@ -0,0 +1,135 @@
+// 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 <google/protobuf/stubs/status.h>
+
+#include <ostream>
+#include <stdint.h>
+#include <stdio.h>
+#include <string>
+#include <utility>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace error {
+inline string CodeEnumToString(error::Code code) {
+ switch (code) {
+ case OK:
+ return "OK";
+ case CANCELLED:
+ return "CANCELLED";
+ case UNKNOWN:
+ return "UNKNOWN";
+ case INVALID_ARGUMENT:
+ return "INVALID_ARGUMENT";
+ case DEADLINE_EXCEEDED:
+ return "DEADLINE_EXCEEDED";
+ case NOT_FOUND:
+ return "NOT_FOUND";
+ case ALREADY_EXISTS:
+ return "ALREADY_EXISTS";
+ case PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case UNAUTHENTICATED:
+ return "UNAUTHENTICATED";
+ case RESOURCE_EXHAUSTED:
+ return "RESOURCE_EXHAUSTED";
+ case FAILED_PRECONDITION:
+ return "FAILED_PRECONDITION";
+ case ABORTED:
+ return "ABORTED";
+ case OUT_OF_RANGE:
+ return "OUT_OF_RANGE";
+ case UNIMPLEMENTED:
+ return "UNIMPLEMENTED";
+ case INTERNAL:
+ return "INTERNAL";
+ case UNAVAILABLE:
+ return "UNAVAILABLE";
+ case DATA_LOSS:
+ return "DATA_LOSS";
+ }
+
+ // No default clause, clang will abort if a code is missing from
+ // above switch.
+ return "UNKNOWN";
+}
+} // namespace error.
+
+const Status Status::OK = Status();
+const Status Status::CANCELLED = Status(error::CANCELLED, "");
+const Status Status::UNKNOWN = Status(error::UNKNOWN, "");
+
+Status::Status() : error_code_(error::OK) {
+}
+
+Status::Status(error::Code error_code, StringPiece error_message)
+ : error_code_(error_code) {
+ if (error_code != error::OK) {
+ error_message_ = error_message.ToString();
+ }
+}
+
+Status::Status(const Status& other)
+ : error_code_(other.error_code_), error_message_(other.error_message_) {
+}
+
+Status& Status::operator=(const Status& other) {
+ error_code_ = other.error_code_;
+ error_message_ = other.error_message_;
+ return *this;
+}
+
+bool Status::operator==(const Status& x) const {
+ return error_code_ == x.error_code_ &&
+ error_message_ == x.error_message_;
+}
+
+string Status::ToString() const {
+ if (error_code_ == error::OK) {
+ return "OK";
+ } else {
+ if (error_message_.empty()) {
+ return error::CodeEnumToString(error_code_);
+ } else {
+ return error::CodeEnumToString(error_code_) + ":" +
+ error_message_;
+ }
+ }
+}
+
+ostream& operator<<(ostream& os, const Status& x) {
+ os << x.ToString();
+ return os;
+}
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/status.h b/src/google/protobuf/stubs/status.h
new file mode 100644
index 00000000..614ab994
--- /dev/null
+++ b/src/google/protobuf/stubs/status.h
@@ -0,0 +1,116 @@
+// 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 GOOGLE_PROTOBUF_STUBS_STATUS_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUS_H_
+
+#include <iosfwd>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace error {
+// These values must match error codes defined in google/rpc/code.proto.
+enum Code {
+ OK = 0,
+ CANCELLED = 1,
+ UNKNOWN = 2,
+ INVALID_ARGUMENT = 3,
+ DEADLINE_EXCEEDED = 4,
+ NOT_FOUND = 5,
+ ALREADY_EXISTS = 6,
+ PERMISSION_DENIED = 7,
+ UNAUTHENTICATED = 16,
+ RESOURCE_EXHAUSTED = 8,
+ FAILED_PRECONDITION = 9,
+ ABORTED = 10,
+ OUT_OF_RANGE = 11,
+ UNIMPLEMENTED = 12,
+ INTERNAL = 13,
+ UNAVAILABLE = 14,
+ DATA_LOSS = 15,
+};
+} // namespace error
+
+class LIBPROTOBUF_EXPORT Status {
+ public:
+ // Creates a "successful" status.
+ Status();
+
+ // Create a status in the canonical error space with the specified
+ // code, and error message. If "code == 0", error_message is
+ // ignored and a Status object identical to Status::OK is
+ // constructed.
+ Status(error::Code error_code, StringPiece error_message);
+ Status(const Status&);
+ Status& operator=(const Status& x);
+ ~Status() {}
+
+ // Some pre-defined Status objects
+ static const Status OK; // Identical to 0-arg constructor
+ static const Status CANCELLED;
+ static const Status UNKNOWN;
+
+ // Accessor
+ bool ok() const {
+ return error_code_ == error::OK;
+ }
+ int error_code() const {
+ return error_code_;
+ }
+ StringPiece error_message() const {
+ return error_message_;
+ }
+
+ bool operator==(const Status& x) const;
+ bool operator!=(const Status& x) const {
+ return !operator==(x);
+ }
+
+ // Return a combination of the error code name and message.
+ string ToString() const;
+
+ private:
+ error::Code error_code_;
+ string error_message_;
+};
+
+// Prints a human-readable representation of 'x' to 'os'.
+LIBPROTOBUF_EXPORT ostream& operator<<(ostream& os, const Status& x);
+
+#define EXPECT_OK(value) EXPECT_TRUE((value).ok())
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
+#endif // GOOGLE_PROTOBUF_STUBS_STATUS_H_
diff --git a/src/google/protobuf/stubs/status_macros.h b/src/google/protobuf/stubs/status_macros.h
new file mode 100644
index 00000000..743e79a7
--- /dev/null
+++ b/src/google/protobuf/stubs/status_macros.h
@@ -0,0 +1,89 @@
+// 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.
+
+// From: util/task/contrib/status_macros/status_macros.h
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUS_MACROS_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUS_MACROS_H_
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+// Run a command that returns a util::Status. If the called code returns an
+// error status, return that status up out of this method too.
+//
+// Example:
+// RETURN_IF_ERROR(DoThings(4));
+#define RETURN_IF_ERROR(expr) \
+ do { \
+ /* Using _status below to avoid capture problems if expr is "status". */ \
+ const ::google::protobuf::util::Status _status = (expr); \
+ if (GOOGLE_PREDICT_FALSE(!_status.ok())) return _status; \
+ } while (0)
+
+// Internal helper for concatenating macro values.
+#define STATUS_MACROS_CONCAT_NAME_INNER(x, y) x##y
+#define STATUS_MACROS_CONCAT_NAME(x, y) STATUS_MACROS_CONCAT_NAME_INNER(x, y)
+
+template<typename T>
+Status DoAssignOrReturn(T& lhs, StatusOr<T> result) {
+ if (result.ok()) {
+ lhs = result.ValueOrDie();
+ }
+ return result.status();
+}
+
+#define ASSIGN_OR_RETURN_IMPL(status, lhs, rexpr) \
+ Status status = DoAssignOrReturn(lhs, (rexpr)); \
+ if (GOOGLE_PREDICT_FALSE(!status.ok())) return status;
+
+// Executes an expression that returns a util::StatusOr, extracting its value
+// into the variable defined by lhs (or returning on error).
+//
+// Example: Assigning to an existing value
+// ValueType value;
+// ASSIGN_OR_RETURN(value, MaybeGetValue(arg));
+//
+// WARNING: ASSIGN_OR_RETURN expands into multiple statements; it cannot be used
+// in a single statement (e.g. as the body of an if statement without {})!
+#define ASSIGN_OR_RETURN(lhs, rexpr) \
+ ASSIGN_OR_RETURN_IMPL( \
+ STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr);
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_STATUS_H_
diff --git a/src/google/protobuf/stubs/status_test.cc b/src/google/protobuf/stubs/status_test.cc
new file mode 100644
index 00000000..c70c33c4
--- /dev/null
+++ b/src/google/protobuf/stubs/status_test.cc
@@ -0,0 +1,131 @@
+// 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 <google/protobuf/stubs/status.h>
+
+#include <stdio.h>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace {
+TEST(Status, Empty) {
+ util::Status status;
+ EXPECT_EQ(util::error::OK, util::Status::OK.error_code());
+ EXPECT_EQ("OK", util::Status::OK.ToString());
+}
+
+TEST(Status, GenericCodes) {
+ EXPECT_EQ(util::error::OK, util::Status::OK.error_code());
+ EXPECT_EQ(util::error::CANCELLED, util::Status::CANCELLED.error_code());
+ EXPECT_EQ(util::error::UNKNOWN, util::Status::UNKNOWN.error_code());
+}
+
+TEST(Status, ConstructorZero) {
+ util::Status status(util::error::OK, "msg");
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ("OK", status.ToString());
+}
+
+TEST(Status, CheckOK) {
+ util::Status status;
+ GOOGLE_CHECK_OK(status);
+ GOOGLE_CHECK_OK(status) << "Failed";
+ GOOGLE_DCHECK_OK(status) << "Failed";
+}
+
+TEST(Status, ErrorMessage) {
+ util::Status status(util::error::INVALID_ARGUMENT, "");
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ("", status.error_message().ToString());
+ EXPECT_EQ("INVALID_ARGUMENT", status.ToString());
+ status = util::Status(util::error::INVALID_ARGUMENT, "msg");
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ("msg", status.error_message().ToString());
+ EXPECT_EQ("INVALID_ARGUMENT:msg", status.ToString());
+ status = util::Status(util::error::OK, "msg");
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ("", status.error_message().ToString());
+ EXPECT_EQ("OK", status.ToString());
+}
+
+TEST(Status, Copy) {
+ util::Status a(util::error::UNKNOWN, "message");
+ util::Status b(a);
+ ASSERT_EQ(a.ToString(), b.ToString());
+}
+
+TEST(Status, Assign) {
+ util::Status a(util::error::UNKNOWN, "message");
+ util::Status b;
+ b = a;
+ ASSERT_EQ(a.ToString(), b.ToString());
+}
+
+TEST(Status, AssignEmpty) {
+ util::Status a(util::error::UNKNOWN, "message");
+ util::Status b;
+ a = b;
+ ASSERT_EQ(string("OK"), a.ToString());
+ ASSERT_TRUE(b.ok());
+ ASSERT_TRUE(a.ok());
+}
+
+TEST(Status, EqualsOK) {
+ ASSERT_EQ(util::Status::OK, util::Status());
+}
+
+TEST(Status, EqualsSame) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = util::Status(util::error::CANCELLED, "message");
+ ASSERT_EQ(a, b);
+}
+
+TEST(Status, EqualsCopy) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = a;
+ ASSERT_EQ(a, b);
+}
+
+TEST(Status, EqualsDifferentCode) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = util::Status(util::error::UNKNOWN, "message");
+ ASSERT_NE(a, b);
+}
+
+TEST(Status, EqualsDifferentMessage) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = util::Status(util::error::CANCELLED, "another");
+ ASSERT_NE(a, b);
+}
+} // namespace
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/statusor.cc b/src/google/protobuf/stubs/statusor.cc
new file mode 100644
index 00000000..48d1402a
--- /dev/null
+++ b/src/google/protobuf/stubs/statusor.cc
@@ -0,0 +1,46 @@
+// 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 <google/protobuf/stubs/statusor.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace internal {
+
+void StatusOrHelper::Crash(const Status& status) {
+ GOOGLE_LOG(FATAL) << "Attempting to fetch value instead of handling error "
+ << status.ToString();
+}
+
+} // namespace internal
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/statusor.h b/src/google/protobuf/stubs/statusor.h
new file mode 100644
index 00000000..a9d2b374
--- /dev/null
+++ b/src/google/protobuf/stubs/statusor.h
@@ -0,0 +1,259 @@
+// 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.
+
+// StatusOr<T> is the union of a Status object and a T
+// object. StatusOr models the concept of an object that is either a
+// usable value, or an error Status explaining why such a value is
+// not present. To this end, StatusOr<T> does not allow its Status
+// value to be Status::OK. Further, StatusOr<T*> does not allow the
+// contained pointer to be NULL.
+//
+// The primary use-case for StatusOr<T> is as the return value of a
+// function which may fail.
+//
+// Example client usage for a StatusOr<T>, where T is not a pointer:
+//
+// StatusOr<float> result = DoBigCalculationThatCouldFail();
+// if (result.ok()) {
+// float answer = result.ValueOrDie();
+// printf("Big calculation yielded: %f", answer);
+// } else {
+// LOG(ERROR) << result.status();
+// }
+//
+// Example client usage for a StatusOr<T*>:
+//
+// StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
+// if (result.ok()) {
+// std::unique_ptr<Foo> foo(result.ValueOrDie());
+// foo->DoSomethingCool();
+// } else {
+// LOG(ERROR) << result.status();
+// }
+//
+// Example client usage for a StatusOr<std::unique_ptr<T>>:
+//
+// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
+// if (result.ok()) {
+// std::unique_ptr<Foo> foo = result.ConsumeValueOrDie();
+// foo->DoSomethingCool();
+// } else {
+// LOG(ERROR) << result.status();
+// }
+//
+// Example factory implementation returning StatusOr<T*>:
+//
+// StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
+// if (arg <= 0) {
+// return ::util::Status(::util::error::INVALID_ARGUMENT,
+// "Arg must be positive");
+// } else {
+// return new Foo(arg);
+// }
+// }
+//
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
+
+#include <new>
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+template<typename T>
+class StatusOr {
+ template<typename U> friend class StatusOr;
+
+ public:
+ // Construct a new StatusOr with Status::UNKNOWN status
+ StatusOr();
+
+ // Construct a new StatusOr with the given non-ok status. After calling
+ // this constructor, calls to ValueOrDie() will CHECK-fail.
+ //
+ // NOTE: Not explicit - we want to use StatusOr<T> as a return
+ // value, so it is convenient and sensible to be able to do 'return
+ // Status()' when the return type is StatusOr<T>.
+ //
+ // REQUIRES: status != Status::OK. This requirement is DCHECKed.
+ // In optimized builds, passing Status::OK here will have the effect
+ // of passing PosixErrorSpace::EINVAL as a fallback.
+ StatusOr(const Status& status); // NOLINT
+
+ // Construct a new StatusOr with the given value. If T is a plain pointer,
+ // value must not be NULL. After calling this constructor, calls to
+ // ValueOrDie() will succeed, and calls to status() will return OK.
+ //
+ // NOTE: Not explicit - we want to use StatusOr<T> as a return type
+ // so it is convenient and sensible to be able to do 'return T()'
+ // when when the return type is StatusOr<T>.
+ //
+ // REQUIRES: if T is a plain pointer, value != NULL. This requirement is
+ // DCHECKed. In optimized builds, passing a NULL pointer here will have
+ // the effect of passing PosixErrorSpace::EINVAL as a fallback.
+ StatusOr(const T& value); // NOLINT
+
+ // Copy constructor.
+ StatusOr(const StatusOr& other);
+
+ // Conversion copy constructor, T must be copy constructible from U
+ template<typename U>
+ StatusOr(const StatusOr<U>& other);
+
+ // Assignment operator.
+ StatusOr& operator=(const StatusOr& other);
+
+ // Conversion assignment operator, T must be assignable from U
+ template<typename U>
+ StatusOr& operator=(const StatusOr<U>& other);
+
+ // Returns a reference to our status. If this contains a T, then
+ // returns Status::OK.
+ const Status& status() const;
+
+ // Returns this->status().ok()
+ bool ok() const;
+
+ // Returns a reference to our current value, or CHECK-fails if !this->ok().
+ // If you need to initialize a T object from the stored value,
+ // ConsumeValueOrDie() may be more efficient.
+ const T& ValueOrDie() const;
+
+ private:
+ Status status_;
+ T value_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation details for StatusOr<T>
+
+namespace internal {
+
+class LIBPROTOBUF_EXPORT StatusOrHelper {
+ public:
+ // Move type-agnostic error handling to the .cc.
+ static void Crash(const util::Status& status);
+
+ // Customized behavior for StatusOr<T> vs. StatusOr<T*>
+ template<typename T>
+ struct Specialize;
+};
+
+template<typename T>
+struct StatusOrHelper::Specialize {
+ // For non-pointer T, a reference can never be NULL.
+ static inline bool IsValueNull(const T& t) { return false; }
+};
+
+template<typename T>
+struct StatusOrHelper::Specialize<T*> {
+ static inline bool IsValueNull(const T* t) { return t == NULL; }
+};
+
+} // namespace internal
+
+template<typename T>
+inline StatusOr<T>::StatusOr()
+ : status_(util::Status::UNKNOWN) {
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const Status& status) {
+ if (status.ok()) {
+ status_ = Status(error::INTERNAL, "Status::OK is not a valid argument.");
+ } else {
+ status_ = status;
+ }
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const T& value) {
+ if (internal::StatusOrHelper::Specialize<T>::IsValueNull(value)) {
+ status_ = Status(error::INTERNAL, "NULL is not a vaild argument.");
+ } else {
+ status_ = Status::OK;
+ value_ = value;
+ }
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const StatusOr<T>& other)
+ : status_(other.status_), value_(other.value_) {
+}
+
+template<typename T>
+inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<T>& other) {
+ status_ = other.status_;
+ value_ = other.value_;
+ return *this;
+}
+
+template<typename T>
+template<typename U>
+inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
+ : status_(other.status_), value_(other.value_) {
+}
+
+template<typename T>
+template<typename U>
+inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
+ status_ = other.status_;
+ value_ = other.value_;
+ return *this;
+}
+
+template<typename T>
+inline const Status& StatusOr<T>::status() const {
+ return status_;
+}
+
+template<typename T>
+inline bool StatusOr<T>::ok() const {
+ return status().ok();
+}
+
+template<typename T>
+inline const T& StatusOr<T>::ValueOrDie() const {
+ if (!status_.ok()) {
+ internal::StatusOrHelper::Crash(status_);
+ }
+ return value_;
+}
+} // namespace util
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
diff --git a/src/google/protobuf/stubs/statusor_test.cc b/src/google/protobuf/stubs/statusor_test.cc
new file mode 100644
index 00000000..6e2a9e55
--- /dev/null
+++ b/src/google/protobuf/stubs/statusor_test.cc
@@ -0,0 +1,274 @@
+// 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 <google/protobuf/stubs/statusor.h>
+
+#include <errno.h>
+#include <memory>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace {
+
+class Base1 {
+ public:
+ virtual ~Base1() {}
+ int pad;
+};
+
+class Base2 {
+ public:
+ virtual ~Base2() {}
+ int yetotherpad;
+};
+
+class Derived : public Base1, public Base2 {
+ public:
+ virtual ~Derived() {}
+ int evenmorepad;
+};
+
+class CopyNoAssign {
+ public:
+ explicit CopyNoAssign(int value) : foo(value) {}
+ CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {}
+ int foo;
+ private:
+ const CopyNoAssign& operator=(const CopyNoAssign&);
+};
+
+TEST(StatusOr, TestDefaultCtor) {
+ StatusOr<int> thing;
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::UNKNOWN, thing.status());
+}
+
+TEST(StatusOr, TestStatusCtor) {
+ StatusOr<int> thing(Status::CANCELLED);
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::CANCELLED, thing.status());
+}
+
+TEST(StatusOr, TestValueCtor) {
+ const int kI = 4;
+ StatusOr<int> thing(kI);
+ EXPECT_TRUE(thing.ok());
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusOk) {
+ const int kI = 4;
+ StatusOr<int> original(kI);
+ StatusOr<int> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOk) {
+ StatusOr<int> original(Status::CANCELLED);
+ StatusOr<int> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestCopyCtorStatusOKConverting) {
+ const int kI = 4;
+ StatusOr<int> original(kI);
+ StatusOr<double> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOkConverting) {
+ StatusOr<int> original(Status::CANCELLED);
+ StatusOr<double> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestAssignmentStatusOk) {
+ const int kI = 4;
+ StatusOr<int> source(kI);
+ StatusOr<int> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestAssignmentStatusNotOk) {
+ StatusOr<int> source(Status::CANCELLED);
+ StatusOr<int> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestAssignmentStatusOKConverting) {
+ const int kI = 4;
+ StatusOr<int> source(kI);
+ StatusOr<double> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_DOUBLE_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestAssignmentStatusNotOkConverting) {
+ StatusOr<int> source(Status::CANCELLED);
+ StatusOr<double> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestStatus) {
+ StatusOr<int> good(4);
+ EXPECT_TRUE(good.ok());
+ StatusOr<int> bad(Status::CANCELLED);
+ EXPECT_FALSE(bad.ok());
+ EXPECT_EQ(Status::CANCELLED, bad.status());
+}
+
+TEST(StatusOr, TestValue) {
+ const int kI = 4;
+ StatusOr<int> thing(kI);
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestValueConst) {
+ const int kI = 4;
+ const StatusOr<int> thing(kI);
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerDefaultCtor) {
+ StatusOr<int*> thing;
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::UNKNOWN, thing.status());
+}
+
+TEST(StatusOr, TestPointerStatusCtor) {
+ StatusOr<int*> thing(Status::CANCELLED);
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::CANCELLED, thing.status());
+}
+
+TEST(StatusOr, TestPointerValueCtor) {
+ const int kI = 4;
+ StatusOr<const int*> thing(&kI);
+ EXPECT_TRUE(thing.ok());
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOk) {
+ const int kI = 0;
+ StatusOr<const int*> original(&kI);
+ StatusOr<const int*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOk) {
+ StatusOr<int*> original(Status::CANCELLED);
+ StatusOr<int*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) {
+ Derived derived;
+ StatusOr<Derived*> original(&derived);
+ StatusOr<Base2*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(static_cast<const Base2*>(original.ValueOrDie()),
+ copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) {
+ StatusOr<Derived*> original(Status::CANCELLED);
+ StatusOr<Base2*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusOk) {
+ const int kI = 0;
+ StatusOr<const int*> source(&kI);
+ StatusOr<const int*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusNotOk) {
+ StatusOr<int*> source(Status::CANCELLED);
+ StatusOr<int*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusOKConverting) {
+ Derived derived;
+ StatusOr<Derived*> source(&derived);
+ StatusOr<Base2*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_EQ(static_cast<const Base2*>(source.ValueOrDie()),
+ target.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) {
+ StatusOr<Derived*> source(Status::CANCELLED);
+ StatusOr<Base2*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestPointerStatus) {
+ const int kI = 0;
+ StatusOr<const int*> good(&kI);
+ EXPECT_TRUE(good.ok());
+ StatusOr<const int*> bad(Status::CANCELLED);
+ EXPECT_EQ(Status::CANCELLED, bad.status());
+}
+
+TEST(StatusOr, TestPointerValue) {
+ const int kI = 0;
+ StatusOr<const int*> thing(&kI);
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerValueConst) {
+ const int kI = 0;
+ const StatusOr<const int*> thing(&kI);
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+} // namespace
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/stringpiece.cc b/src/google/protobuf/stubs/stringpiece.cc
new file mode 100644
index 00000000..989474b7
--- /dev/null
+++ b/src/google/protobuf/stubs/stringpiece.cc
@@ -0,0 +1,268 @@
+// 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 <google/protobuf/stubs/stringpiece.h>
+
+#include <string.h>
+#include <algorithm>
+#include <climits>
+#include <string>
+#include <ostream>
+
+namespace google {
+namespace protobuf {
+std::ostream& operator<<(std::ostream& o, StringPiece piece) {
+ o.write(piece.data(), piece.size());
+ return o;
+}
+
+// Out-of-line error path.
+void StringPiece::LogFatalSizeTooBig(size_t size, const char* details) {
+ GOOGLE_LOG(FATAL) << "size too big: " << size << " details: " << details;
+}
+
+StringPiece::StringPiece(StringPiece x, stringpiece_ssize_type pos)
+ : ptr_(x.ptr_ + pos), length_(x.length_ - pos) {
+ GOOGLE_DCHECK_LE(0, pos);
+ GOOGLE_DCHECK_LE(pos, x.length_);
+}
+
+StringPiece::StringPiece(StringPiece x,
+ stringpiece_ssize_type pos,
+ stringpiece_ssize_type len)
+ : ptr_(x.ptr_ + pos), length_(std::min(len, x.length_ - pos)) {
+ GOOGLE_DCHECK_LE(0, pos);
+ GOOGLE_DCHECK_LE(pos, x.length_);
+ GOOGLE_DCHECK_GE(len, 0);
+}
+
+void StringPiece::CopyToString(string* target) const {
+ target->assign(ptr_, length_);
+}
+
+void StringPiece::AppendToString(string* target) const {
+ target->append(ptr_, length_);
+}
+
+bool StringPiece::Consume(StringPiece x) {
+ if (starts_with(x)) {
+ ptr_ += x.length_;
+ length_ -= x.length_;
+ return true;
+ }
+ return false;
+}
+
+bool StringPiece::ConsumeFromEnd(StringPiece x) {
+ if (ends_with(x)) {
+ length_ -= x.length_;
+ return true;
+ }
+ return false;
+}
+
+stringpiece_ssize_type StringPiece::copy(char* buf,
+ size_type n,
+ size_type pos) const {
+ stringpiece_ssize_type ret = std::min(length_ - pos, n);
+ memcpy(buf, ptr_ + pos, ret);
+ return ret;
+}
+
+bool StringPiece::contains(StringPiece s) const {
+ return find(s, 0) != npos;
+}
+
+stringpiece_ssize_type StringPiece::find(StringPiece s, size_type pos) const {
+ if (length_ <= 0 || pos > static_cast<size_type>(length_)) {
+ if (length_ == 0 && pos == 0 && s.length_ == 0) return 0;
+ return npos;
+ }
+ const char *result = std::search(ptr_ + pos, ptr_ + length_,
+ s.ptr_, s.ptr_ + s.length_);
+ return result == ptr_ + length_ ? npos : result - ptr_;
+}
+
+stringpiece_ssize_type StringPiece::find(char c, size_type pos) const {
+ if (length_ <= 0 || pos >= static_cast<size_type>(length_)) {
+ return npos;
+ }
+ const char* result = static_cast<const char*>(
+ memchr(ptr_ + pos, c, length_ - pos));
+ return result != NULL ? result - ptr_ : npos;
+}
+
+stringpiece_ssize_type StringPiece::rfind(StringPiece s, size_type pos) const {
+ if (length_ < s.length_) return npos;
+ const size_t ulen = length_;
+ if (s.length_ == 0) return std::min(ulen, pos);
+
+ const char* last = ptr_ + std::min(ulen - s.length_, pos) + s.length_;
+ const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+ return result != last ? result - ptr_ : npos;
+}
+
+// Search range is [0..pos] inclusive. If pos == npos, search everything.
+stringpiece_ssize_type StringPiece::rfind(char c, size_type pos) const {
+ // Note: memrchr() is not available on Windows.
+ if (length_ <= 0) return npos;
+ for (stringpiece_ssize_type i =
+ std::min(pos, static_cast<size_type>(length_ - 1));
+ i >= 0; --i) {
+ if (ptr_[i] == c) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table. This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char. Thus it should be be declared
+// as follows:
+// bool table[UCHAR_MAX + 1]
+static inline void BuildLookupTable(StringPiece characters_wanted,
+ bool* table) {
+ const stringpiece_ssize_type length = characters_wanted.length();
+ const char* const data = characters_wanted.data();
+ for (stringpiece_ssize_type i = 0; i < length; ++i) {
+ table[static_cast<unsigned char>(data[i])] = true;
+ }
+}
+
+stringpiece_ssize_type StringPiece::find_first_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0 || s.length_ <= 0) {
+ return npos;
+ }
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (stringpiece_ssize_type i = pos; i < length_; ++i) {
+ if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_first_not_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+ if (s.length_ <= 0) return 0;
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (stringpiece_ssize_type i = pos; i < length_; ++i) {
+ if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_first_not_of(char c,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+
+ for (; pos < static_cast<size_type>(length_); ++pos) {
+ if (ptr_[pos] != c) {
+ return pos;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_last_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0 || s.length_ <= 0) return npos;
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (stringpiece_ssize_type i =
+ std::min(pos, static_cast<size_type>(length_ - 1)); i >= 0; --i) {
+ if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_last_not_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+
+ stringpiece_ssize_type i = std::min(pos, static_cast<size_type>(length_ - 1));
+ if (s.length_ <= 0) return i;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (; i >= 0; --i) {
+ if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_last_not_of(char c,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+
+ for (stringpiece_ssize_type i =
+ std::min(pos, static_cast<size_type>(length_ - 1)); i >= 0; --i) {
+ if (ptr_[i] != c) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+ if (pos > length_) pos = length_;
+ if (n > length_ - pos) n = length_ - pos;
+ return StringPiece(ptr_ + pos, n);
+}
+
+const StringPiece::size_type StringPiece::npos = size_type(-1);
+
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/stringpiece.h b/src/google/protobuf/stubs/stringpiece.h
new file mode 100644
index 00000000..353a60d3
--- /dev/null
+++ b/src/google/protobuf/stubs/stringpiece.h
@@ -0,0 +1,440 @@
+// 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.
+
+// A StringPiece points to part or all of a string, Cord, double-quoted string
+// literal, or other string-like object. A StringPiece does *not* own the
+// string to which it points. A StringPiece is not null-terminated.
+//
+// You can use StringPiece as a function or method parameter. A StringPiece
+// parameter can receive a double-quoted string literal argument, a "const
+// char*" argument, a string argument, or a StringPiece argument with no data
+// copying. Systematic use of StringPiece for arguments reduces data
+// copies and strlen() calls.
+//
+// Prefer passing StringPieces by value:
+// void MyFunction(StringPiece arg);
+// If circumstances require, you may also pass by const reference:
+// void MyFunction(const StringPiece& arg); // not preferred
+// Both of these have the same lifetime semantics. Passing by value
+// generates slightly smaller code. For more discussion, see the thread
+// go/stringpiecebyvalue on c-users.
+//
+// StringPiece is also suitable for local variables if you know that
+// the lifetime of the underlying object is longer than the lifetime
+// of your StringPiece variable.
+//
+// Beware of binding a StringPiece to a temporary:
+// StringPiece sp = obj.MethodReturningString(); // BAD: lifetime problem
+//
+// This code is okay:
+// string str = obj.MethodReturningString(); // str owns its contents
+// StringPiece sp(str); // GOOD, because str outlives sp
+//
+// StringPiece is sometimes a poor choice for a return value and usually a poor
+// choice for a data member. If you do use a StringPiece this way, it is your
+// responsibility to ensure that the object pointed to by the StringPiece
+// outlives the StringPiece.
+//
+// A StringPiece may represent just part of a string; thus the name "Piece".
+// For example, when splitting a string, vector<StringPiece> is a natural data
+// type for the output. For another example, a Cord is a non-contiguous,
+// potentially very long string-like object. The Cord class has an interface
+// that iteratively provides StringPiece objects that point to the
+// successive pieces of a Cord object.
+//
+// A StringPiece is not null-terminated. If you write code that scans a
+// StringPiece, you must check its length before reading any characters.
+// Common idioms that work on null-terminated strings do not work on
+// StringPiece objects.
+//
+// There are several ways to create a null StringPiece:
+// StringPiece()
+// StringPiece(NULL)
+// StringPiece(NULL, 0)
+// For all of the above, sp.data() == NULL, sp.length() == 0,
+// and sp.empty() == true. Also, if you create a StringPiece with
+// a non-NULL pointer then sp.data() != NULL. Once created,
+// sp.data() will stay either NULL or not-NULL, except if you call
+// sp.clear() or sp.set().
+//
+// Thus, you can use StringPiece(NULL) to signal an out-of-band value
+// that is different from other StringPiece values. This is similar
+// to the way that const char* p1 = NULL; is different from
+// const char* p2 = "";.
+//
+// There are many ways to create an empty StringPiece:
+// StringPiece()
+// StringPiece(NULL)
+// StringPiece(NULL, 0)
+// StringPiece("")
+// StringPiece("", 0)
+// StringPiece("abcdef", 0)
+// StringPiece("abcdef"+6, 0)
+// For all of the above, sp.length() will be 0 and sp.empty() will be true.
+// For some empty StringPiece values, sp.data() will be NULL.
+// For some empty StringPiece values, sp.data() will not be NULL.
+//
+// Be careful not to confuse: null StringPiece and empty StringPiece.
+// The set of empty StringPieces properly includes the set of null StringPieces.
+// That is, every null StringPiece is an empty StringPiece,
+// but some non-null StringPieces are empty Stringpieces too.
+//
+// All empty StringPiece values compare equal to each other.
+// Even a null StringPieces compares equal to a non-null empty StringPiece:
+// StringPiece() == StringPiece("", 0)
+// StringPiece(NULL) == StringPiece("abc", 0)
+// StringPiece(NULL, 0) == StringPiece("abcdef"+6, 0)
+//
+// Look carefully at this example:
+// StringPiece("") == NULL
+// True or false? TRUE, because StringPiece::operator== converts
+// the right-hand side from NULL to StringPiece(NULL),
+// and then compares two zero-length spans of characters.
+// However, we are working to make this example produce a compile error.
+//
+// Suppose you want to write:
+// bool TestWhat?(StringPiece sp) { return sp == NULL; } // BAD
+// Do not do that. Write one of these instead:
+// bool TestNull(StringPiece sp) { return sp.data() == NULL; }
+// bool TestEmpty(StringPiece sp) { return sp.empty(); }
+// The intent of TestWhat? is unclear. Did you mean TestNull or TestEmpty?
+// Right now, TestWhat? behaves likes TestEmpty.
+// We are working to make TestWhat? produce a compile error.
+// TestNull is good to test for an out-of-band signal.
+// TestEmpty is good to test for an empty StringPiece.
+//
+// Caveats (again):
+// (1) The lifetime of the pointed-to string (or piece of a string)
+// must be longer than the lifetime of the StringPiece.
+// (2) There may or may not be a '\0' character after the end of
+// StringPiece data.
+// (3) A null StringPiece is empty.
+// An empty StringPiece may or may not be a null StringPiece.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STRINGPIECE_H_
+#define GOOGLE_PROTOBUF_STUBS_STRINGPIECE_H_
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <iosfwd>
+#include <limits>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+// StringPiece has *two* size types.
+// StringPiece::size_type
+// is unsigned
+// is 32 bits in LP32, 64 bits in LP64, 64 bits in LLP64
+// no future changes intended
+// stringpiece_ssize_type
+// is signed
+// is 32 bits in LP32, 64 bits in LP64, 64 bits in LLP64
+// future changes intended: http://go/64BitStringPiece
+//
+typedef string::difference_type stringpiece_ssize_type;
+
+// STRINGPIECE_CHECK_SIZE protects us from 32-bit overflows.
+// TODO(mec): delete this after stringpiece_ssize_type goes 64 bit.
+#if !defined(NDEBUG)
+#define STRINGPIECE_CHECK_SIZE 1
+#elif defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
+#define STRINGPIECE_CHECK_SIZE 1
+#else
+#define STRINGPIECE_CHECK_SIZE 0
+#endif
+
+class LIBPROTOBUF_EXPORT StringPiece {
+ private:
+ const char* ptr_;
+ stringpiece_ssize_type length_;
+
+ // Prevent overflow in debug mode or fortified mode.
+ // sizeof(stringpiece_ssize_type) may be smaller than sizeof(size_t).
+ static stringpiece_ssize_type CheckedSsizeTFromSizeT(size_t size) {
+#if STRINGPIECE_CHECK_SIZE > 0
+#ifdef max
+#undef max
+#endif
+ if (size > static_cast<size_t>(
+ std::numeric_limits<stringpiece_ssize_type>::max())) {
+ // Some people grep for this message in logs
+ // so take care if you ever change it.
+ LogFatalSizeTooBig(size, "size_t to int conversion");
+ }
+#endif
+ return static_cast<stringpiece_ssize_type>(size);
+ }
+
+ // Out-of-line error path.
+ static void LogFatalSizeTooBig(size_t size, const char* details);
+
+ public:
+ // We provide non-explicit singleton constructors so users can pass
+ // in a "const char*" or a "string" wherever a "StringPiece" is
+ // expected.
+ //
+ // Style guide exception granted:
+ // http://goto/style-guide-exception-20978288
+ StringPiece() : ptr_(NULL), length_(0) {}
+
+ StringPiece(const char* str) // NOLINT(runtime/explicit)
+ : ptr_(str), length_(0) {
+ if (str != NULL) {
+ length_ = CheckedSsizeTFromSizeT(strlen(str));
+ }
+ }
+
+ template <class Allocator>
+ StringPiece( // NOLINT(runtime/explicit)
+ const std::basic_string<char, std::char_traits<char>, Allocator>& str)
+ : ptr_(str.data()), length_(0) {
+ length_ = CheckedSsizeTFromSizeT(str.size());
+ }
+#if defined(HAS_GLOBAL_STRING)
+ template <class Allocator>
+ StringPiece( // NOLINT(runtime/explicit)
+ const basic_string<char, std::char_traits<char>, Allocator>& str)
+ : ptr_(str.data()), length_(0) {
+ length_ = CheckedSsizeTFromSizeT(str.size());
+ }
+#endif
+
+ StringPiece(const char* offset, stringpiece_ssize_type len)
+ : ptr_(offset), length_(len) {
+ assert(len >= 0);
+ }
+
+ // Substring of another StringPiece.
+ // pos must be non-negative and <= x.length().
+ StringPiece(StringPiece x, stringpiece_ssize_type pos);
+ // Substring of another StringPiece.
+ // pos must be non-negative and <= x.length().
+ // len must be non-negative and will be pinned to at most x.length() - pos.
+ StringPiece(StringPiece x,
+ stringpiece_ssize_type pos,
+ stringpiece_ssize_type len);
+
+ // data() may return a pointer to a buffer with embedded NULs, and the
+ // returned buffer may or may not be null terminated. Therefore it is
+ // typically a mistake to pass data() to a routine that expects a NUL
+ // terminated string.
+ const char* data() const { return ptr_; }
+ stringpiece_ssize_type size() const { return length_; }
+ stringpiece_ssize_type length() const { return length_; }
+ bool empty() const { return length_ == 0; }
+
+ void clear() {
+ ptr_ = NULL;
+ length_ = 0;
+ }
+
+ void set(const char* data, stringpiece_ssize_type len) {
+ assert(len >= 0);
+ ptr_ = data;
+ length_ = len;
+ }
+
+ void set(const char* str) {
+ ptr_ = str;
+ if (str != NULL)
+ length_ = CheckedSsizeTFromSizeT(strlen(str));
+ else
+ length_ = 0;
+ }
+
+ void set(const void* data, stringpiece_ssize_type len) {
+ ptr_ = reinterpret_cast<const char*>(data);
+ length_ = len;
+ }
+
+ char operator[](stringpiece_ssize_type i) const {
+ assert(0 <= i);
+ assert(i < length_);
+ return ptr_[i];
+ }
+
+ void remove_prefix(stringpiece_ssize_type n) {
+ assert(length_ >= n);
+ ptr_ += n;
+ length_ -= n;
+ }
+
+ void remove_suffix(stringpiece_ssize_type n) {
+ assert(length_ >= n);
+ length_ -= n;
+ }
+
+ // returns {-1, 0, 1}
+ int compare(StringPiece x) const {
+ const stringpiece_ssize_type min_size =
+ length_ < x.length_ ? length_ : x.length_;
+ int r = memcmp(ptr_, x.ptr_, min_size);
+ if (r < 0) return -1;
+ if (r > 0) return 1;
+ if (length_ < x.length_) return -1;
+ if (length_ > x.length_) return 1;
+ return 0;
+ }
+
+ string as_string() const {
+ return ToString();
+ }
+ // We also define ToString() here, since many other string-like
+ // interfaces name the routine that converts to a C++ string
+ // "ToString", and it's confusing to have the method that does that
+ // for a StringPiece be called "as_string()". We also leave the
+ // "as_string()" method defined here for existing code.
+ string ToString() const {
+ if (ptr_ == NULL) return string();
+ return string(data(), size());
+ }
+
+ operator string() const {
+ return ToString();
+ }
+
+ void CopyToString(string* target) const;
+ void AppendToString(string* target) const;
+
+ bool starts_with(StringPiece x) const {
+ return (length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0);
+ }
+
+ bool ends_with(StringPiece x) const {
+ return ((length_ >= x.length_) &&
+ (memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+ }
+
+ // Checks whether StringPiece starts with x and if so advances the beginning
+ // of it to past the match. It's basically a shortcut for starts_with
+ // followed by remove_prefix.
+ bool Consume(StringPiece x);
+ // Like above but for the end of the string.
+ bool ConsumeFromEnd(StringPiece x);
+
+ // standard STL container boilerplate
+ typedef char value_type;
+ typedef const char* pointer;
+ typedef const char& reference;
+ typedef const char& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ static const size_type npos;
+ typedef const char* const_iterator;
+ typedef const char* iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ iterator begin() const { return ptr_; }
+ iterator end() const { return ptr_ + length_; }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(ptr_ + length_);
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(ptr_);
+ }
+ stringpiece_ssize_type max_size() const { return length_; }
+ stringpiece_ssize_type capacity() const { return length_; }
+
+ // cpplint.py emits a false positive [build/include_what_you_use]
+ stringpiece_ssize_type copy(char* buf, size_type n, size_type pos = 0) const; // NOLINT
+
+ bool contains(StringPiece s) const;
+
+ stringpiece_ssize_type find(StringPiece s, size_type pos = 0) const;
+ stringpiece_ssize_type find(char c, size_type pos = 0) const;
+ stringpiece_ssize_type rfind(StringPiece s, size_type pos = npos) const;
+ stringpiece_ssize_type rfind(char c, size_type pos = npos) const;
+
+ stringpiece_ssize_type find_first_of(StringPiece s, size_type pos = 0) const;
+ stringpiece_ssize_type find_first_of(char c, size_type pos = 0) const {
+ return find(c, pos);
+ }
+ stringpiece_ssize_type find_first_not_of(StringPiece s,
+ size_type pos = 0) const;
+ stringpiece_ssize_type find_first_not_of(char c, size_type pos = 0) const;
+ stringpiece_ssize_type find_last_of(StringPiece s,
+ size_type pos = npos) const;
+ stringpiece_ssize_type find_last_of(char c, size_type pos = npos) const {
+ return rfind(c, pos);
+ }
+ stringpiece_ssize_type find_last_not_of(StringPiece s,
+ size_type pos = npos) const;
+ stringpiece_ssize_type find_last_not_of(char c, size_type pos = npos) const;
+
+ StringPiece substr(size_type pos, size_type n = npos) const;
+};
+
+// This large function is defined inline so that in a fairly common case where
+// one of the arguments is a literal, the compiler can elide a lot of the
+// following comparisons.
+inline bool operator==(StringPiece x, StringPiece y) {
+ stringpiece_ssize_type len = x.size();
+ if (len != y.size()) {
+ return false;
+ }
+
+ return x.data() == y.data() || len <= 0 ||
+ memcmp(x.data(), y.data(), len) == 0;
+}
+
+inline bool operator!=(StringPiece x, StringPiece y) {
+ return !(x == y);
+}
+
+inline bool operator<(StringPiece x, StringPiece y) {
+ const stringpiece_ssize_type min_size =
+ x.size() < y.size() ? x.size() : y.size();
+ const int r = memcmp(x.data(), y.data(), min_size);
+ return (r < 0) || (r == 0 && x.size() < y.size());
+}
+
+inline bool operator>(StringPiece x, StringPiece y) {
+ return y < x;
+}
+
+inline bool operator<=(StringPiece x, StringPiece y) {
+ return !(x > y);
+}
+
+inline bool operator>=(StringPiece x, StringPiece y) {
+ return !(x < y);
+}
+
+// allow StringPiece to be logged
+extern std::ostream& operator<<(std::ostream& o, StringPiece piece);
+
+} // namespace protobuf
+} // namespace google
+
+#endif // STRINGS_STRINGPIECE_H_
diff --git a/src/google/protobuf/stubs/stringpiece_unittest.cc b/src/google/protobuf/stubs/stringpiece_unittest.cc
new file mode 100644
index 00000000..9b5dae13
--- /dev/null
+++ b/src/google/protobuf/stubs/stringpiece_unittest.cc
@@ -0,0 +1,793 @@
+// 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 <google/protobuf/stubs/stringpiece.h>
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace {
+TEST(StringPiece, Ctor) {
+ {
+ // Null.
+ StringPiece s10;
+ EXPECT_TRUE(s10.data() == NULL);
+ EXPECT_EQ(0, s10.length());
+ }
+
+ {
+ // const char* without length.
+ const char* hello = "hello";
+ StringPiece s20(hello);
+ EXPECT_TRUE(s20.data() == hello);
+ EXPECT_EQ(5, s20.length());
+
+ // const char* with length.
+ StringPiece s21(hello, 4);
+ EXPECT_TRUE(s21.data() == hello);
+ EXPECT_EQ(4, s21.length());
+
+ // Not recommended, but valid C++
+ StringPiece s22(hello, 6);
+ EXPECT_TRUE(s22.data() == hello);
+ EXPECT_EQ(6, s22.length());
+ }
+
+ {
+ // std::string.
+ std::string hola = "hola";
+ StringPiece s30(hola);
+ EXPECT_TRUE(s30.data() == hola.data());
+ EXPECT_EQ(4, s30.length());
+
+ // std::string with embedded '\0'.
+ hola.push_back('\0');
+ hola.append("h2");
+ hola.push_back('\0');
+ StringPiece s31(hola);
+ EXPECT_TRUE(s31.data() == hola.data());
+ EXPECT_EQ(8, s31.length());
+ }
+
+#if defined(HAS_GLOBAL_STRING)
+ {
+ // ::string
+ string bonjour = "bonjour";
+ StringPiece s40(bonjour);
+ EXPECT_TRUE(s40.data() == bonjour.data());
+ EXPECT_EQ(7, s40.length());
+ }
+#endif
+
+ // TODO(mec): StringPiece(StringPiece x, int pos);
+ // TODO(mec): StringPiece(StringPiece x, int pos, int len);
+ // TODO(mec): StringPiece(const StringPiece&);
+}
+
+TEST(StringPiece, STLComparator) {
+ string s1("foo");
+ string s2("bar");
+ string s3("baz");
+
+ StringPiece p1(s1);
+ StringPiece p2(s2);
+ StringPiece p3(s3);
+
+ typedef std::map<StringPiece, int> TestMap;
+ TestMap map;
+
+ map.insert(std::make_pair(p1, 0));
+ map.insert(std::make_pair(p2, 1));
+ map.insert(std::make_pair(p3, 2));
+ EXPECT_EQ(map.size(), 3);
+
+ TestMap::const_iterator iter = map.begin();
+ EXPECT_EQ(iter->second, 1);
+ ++iter;
+ EXPECT_EQ(iter->second, 2);
+ ++iter;
+ EXPECT_EQ(iter->second, 0);
+ ++iter;
+ EXPECT_TRUE(iter == map.end());
+
+ TestMap::iterator new_iter = map.find("zot");
+ EXPECT_TRUE(new_iter == map.end());
+
+ new_iter = map.find("bar");
+ EXPECT_TRUE(new_iter != map.end());
+
+ map.erase(new_iter);
+ EXPECT_EQ(map.size(), 2);
+
+ iter = map.begin();
+ EXPECT_EQ(iter->second, 2);
+ ++iter;
+ EXPECT_EQ(iter->second, 0);
+ ++iter;
+ EXPECT_TRUE(iter == map.end());
+}
+
+TEST(StringPiece, ComparisonOperators) {
+#define COMPARE(result, op, x, y) \
+ EXPECT_EQ(result, StringPiece((x)) op StringPiece((y))); \
+ EXPECT_EQ(result, StringPiece((x)).compare(StringPiece((y))) op 0)
+
+ COMPARE(true, ==, "", "");
+ COMPARE(true, ==, "", NULL);
+ COMPARE(true, ==, NULL, "");
+ COMPARE(true, ==, "a", "a");
+ COMPARE(true, ==, "aa", "aa");
+ COMPARE(false, ==, "a", "");
+ COMPARE(false, ==, "", "a");
+ COMPARE(false, ==, "a", "b");
+ COMPARE(false, ==, "a", "aa");
+ COMPARE(false, ==, "aa", "a");
+
+ COMPARE(false, !=, "", "");
+ COMPARE(false, !=, "a", "a");
+ COMPARE(false, !=, "aa", "aa");
+ COMPARE(true, !=, "a", "");
+ COMPARE(true, !=, "", "a");
+ COMPARE(true, !=, "a", "b");
+ COMPARE(true, !=, "a", "aa");
+ COMPARE(true, !=, "aa", "a");
+
+ COMPARE(true, <, "a", "b");
+ COMPARE(true, <, "a", "aa");
+ COMPARE(true, <, "aa", "b");
+ COMPARE(true, <, "aa", "bb");
+ COMPARE(false, <, "a", "a");
+ COMPARE(false, <, "b", "a");
+ COMPARE(false, <, "aa", "a");
+ COMPARE(false, <, "b", "aa");
+ COMPARE(false, <, "bb", "aa");
+
+ COMPARE(true, <=, "a", "a");
+ COMPARE(true, <=, "a", "b");
+ COMPARE(true, <=, "a", "aa");
+ COMPARE(true, <=, "aa", "b");
+ COMPARE(true, <=, "aa", "bb");
+ COMPARE(false, <=, "b", "a");
+ COMPARE(false, <=, "aa", "a");
+ COMPARE(false, <=, "b", "aa");
+ COMPARE(false, <=, "bb", "aa");
+
+ COMPARE(false, >=, "a", "b");
+ COMPARE(false, >=, "a", "aa");
+ COMPARE(false, >=, "aa", "b");
+ COMPARE(false, >=, "aa", "bb");
+ COMPARE(true, >=, "a", "a");
+ COMPARE(true, >=, "b", "a");
+ COMPARE(true, >=, "aa", "a");
+ COMPARE(true, >=, "b", "aa");
+ COMPARE(true, >=, "bb", "aa");
+
+ COMPARE(false, >, "a", "a");
+ COMPARE(false, >, "a", "b");
+ COMPARE(false, >, "a", "aa");
+ COMPARE(false, >, "aa", "b");
+ COMPARE(false, >, "aa", "bb");
+ COMPARE(true, >, "b", "a");
+ COMPARE(true, >, "aa", "a");
+ COMPARE(true, >, "b", "aa");
+ COMPARE(true, >, "bb", "aa");
+
+ string x;
+ for (int i = 0; i < 256; i++) {
+ x += 'a';
+ string y = x;
+ COMPARE(true, ==, x, y);
+ for (int j = 0; j < i; j++) {
+ string z = x;
+ z[j] = 'b'; // Differs in position 'j'
+ COMPARE(false, ==, x, z);
+ COMPARE(true, <, x, z);
+ COMPARE(true, >, z, x);
+ if (j + 1 < i) {
+ z[j + 1] = 'A'; // Differs in position 'j+1' as well
+ COMPARE(false, ==, x, z);
+ COMPARE(true, <, x, z);
+ COMPARE(true, >, z, x);
+ z[j + 1] = 'z'; // Differs in position 'j+1' as well
+ COMPARE(false, ==, x, z);
+ COMPARE(true, <, x, z);
+ COMPARE(true, >, z, x);
+ }
+ }
+ }
+
+#undef COMPARE
+}
+
+TEST(StringPiece, STL1) {
+ const StringPiece a("abcdefghijklmnopqrstuvwxyz");
+ const StringPiece b("abc");
+ const StringPiece c("xyz");
+ const StringPiece d("foobar");
+ const StringPiece e;
+ string temp("123");
+ temp += '\0';
+ temp += "456";
+ const StringPiece f(temp);
+
+ EXPECT_EQ(a[6], 'g');
+ EXPECT_EQ(b[0], 'a');
+ EXPECT_EQ(c[2], 'z');
+ EXPECT_EQ(f[3], '\0');
+ EXPECT_EQ(f[5], '5');
+
+ EXPECT_EQ(*d.data(), 'f');
+ EXPECT_EQ(d.data()[5], 'r');
+ EXPECT_TRUE(e.data() == NULL);
+
+ EXPECT_EQ(*a.begin(), 'a');
+ EXPECT_EQ(*(b.begin() + 2), 'c');
+ EXPECT_EQ(*(c.end() - 1), 'z');
+
+ EXPECT_EQ(*a.rbegin(), 'z');
+ EXPECT_EQ(*(b.rbegin() + 2), 'a');
+ EXPECT_EQ(*(c.rend() - 1), 'x');
+ EXPECT_TRUE(a.rbegin() + 26 == a.rend());
+
+ EXPECT_EQ(a.size(), 26);
+ EXPECT_EQ(b.size(), 3);
+ EXPECT_EQ(c.size(), 3);
+ EXPECT_EQ(d.size(), 6);
+ EXPECT_EQ(e.size(), 0);
+ EXPECT_EQ(f.size(), 7);
+
+ EXPECT_TRUE(!d.empty());
+ EXPECT_TRUE(d.begin() != d.end());
+ EXPECT_TRUE(d.begin() + 6 == d.end());
+
+ EXPECT_TRUE(e.empty());
+ EXPECT_TRUE(e.begin() == e.end());
+
+ EXPECT_GE(a.max_size(), a.capacity());
+ EXPECT_GE(a.capacity(), a.size());
+
+ char buf[4] = { '%', '%', '%', '%' };
+ EXPECT_EQ(a.copy(buf, 4), 4);
+ EXPECT_EQ(buf[0], a[0]);
+ EXPECT_EQ(buf[1], a[1]);
+ EXPECT_EQ(buf[2], a[2]);
+ EXPECT_EQ(buf[3], a[3]);
+ EXPECT_EQ(a.copy(buf, 3, 7), 3);
+ EXPECT_EQ(buf[0], a[7]);
+ EXPECT_EQ(buf[1], a[8]);
+ EXPECT_EQ(buf[2], a[9]);
+ EXPECT_EQ(buf[3], a[3]);
+ EXPECT_EQ(c.copy(buf, 99), 3);
+ EXPECT_EQ(buf[0], c[0]);
+ EXPECT_EQ(buf[1], c[1]);
+ EXPECT_EQ(buf[2], c[2]);
+ EXPECT_EQ(buf[3], a[3]);
+}
+
+// Separated from STL1() because some compilers produce an overly
+// large stack frame for the combined function.
+TEST(StringPiece, STL2) {
+ const StringPiece a("abcdefghijklmnopqrstuvwxyz");
+ const StringPiece b("abc");
+ const StringPiece c("xyz");
+ StringPiece d("foobar");
+ const StringPiece e;
+ const StringPiece f("123" "\0" "456", 7);
+
+ d.clear();
+ EXPECT_EQ(d.size(), 0);
+ EXPECT_TRUE(d.empty());
+ EXPECT_TRUE(d.data() == NULL);
+ EXPECT_TRUE(d.begin() == d.end());
+
+ EXPECT_EQ(StringPiece::npos, string::npos);
+
+ EXPECT_EQ(a.find(b), 0);
+ EXPECT_EQ(a.find(b, 1), StringPiece::npos);
+ EXPECT_EQ(a.find(c), 23);
+ EXPECT_EQ(a.find(c, 9), 23);
+ EXPECT_EQ(a.find(c, StringPiece::npos), StringPiece::npos);
+ EXPECT_EQ(b.find(c), StringPiece::npos);
+ EXPECT_EQ(b.find(c, StringPiece::npos), StringPiece::npos);
+ EXPECT_EQ(a.find(d), 0);
+ EXPECT_EQ(a.find(e), 0);
+ EXPECT_EQ(a.find(d, 12), 12);
+ EXPECT_EQ(a.find(e, 17), 17);
+ StringPiece g("xx not found bb");
+ EXPECT_EQ(a.find(g), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(d.find(b), StringPiece::npos);
+ EXPECT_EQ(e.find(b), StringPiece::npos);
+ EXPECT_EQ(d.find(b, 4), StringPiece::npos);
+ EXPECT_EQ(e.find(b, 7), StringPiece::npos);
+
+ size_t empty_search_pos = string().find(string());
+ EXPECT_EQ(d.find(d), empty_search_pos);
+ EXPECT_EQ(d.find(e), empty_search_pos);
+ EXPECT_EQ(e.find(d), empty_search_pos);
+ EXPECT_EQ(e.find(e), empty_search_pos);
+ EXPECT_EQ(d.find(d, 4), string().find(string(), 4));
+ EXPECT_EQ(d.find(e, 4), string().find(string(), 4));
+ EXPECT_EQ(e.find(d, 4), string().find(string(), 4));
+ EXPECT_EQ(e.find(e, 4), string().find(string(), 4));
+
+ EXPECT_EQ(a.find('a'), 0);
+ EXPECT_EQ(a.find('c'), 2);
+ EXPECT_EQ(a.find('z'), 25);
+ EXPECT_EQ(a.find('$'), StringPiece::npos);
+ EXPECT_EQ(a.find('\0'), StringPiece::npos);
+ EXPECT_EQ(f.find('\0'), 3);
+ EXPECT_EQ(f.find('3'), 2);
+ EXPECT_EQ(f.find('5'), 5);
+ EXPECT_EQ(g.find('o'), 4);
+ EXPECT_EQ(g.find('o', 4), 4);
+ EXPECT_EQ(g.find('o', 5), 8);
+ EXPECT_EQ(a.find('b', 5), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(d.find('\0'), StringPiece::npos);
+ EXPECT_EQ(e.find('\0'), StringPiece::npos);
+ EXPECT_EQ(d.find('\0', 4), StringPiece::npos);
+ EXPECT_EQ(e.find('\0', 7), StringPiece::npos);
+ EXPECT_EQ(d.find('x'), StringPiece::npos);
+ EXPECT_EQ(e.find('x'), StringPiece::npos);
+ EXPECT_EQ(d.find('x', 4), StringPiece::npos);
+ EXPECT_EQ(e.find('x', 7), StringPiece::npos);
+
+ EXPECT_EQ(a.rfind(b), 0);
+ EXPECT_EQ(a.rfind(b, 1), 0);
+ EXPECT_EQ(a.rfind(c), 23);
+ EXPECT_EQ(a.rfind(c, 22), StringPiece::npos);
+ EXPECT_EQ(a.rfind(c, 1), StringPiece::npos);
+ EXPECT_EQ(a.rfind(c, 0), StringPiece::npos);
+ EXPECT_EQ(b.rfind(c), StringPiece::npos);
+ EXPECT_EQ(b.rfind(c, 0), StringPiece::npos);
+ EXPECT_EQ(a.rfind(d), a.as_string().rfind(string()));
+ EXPECT_EQ(a.rfind(e), a.as_string().rfind(string()));
+ EXPECT_EQ(a.rfind(d, 12), 12);
+ EXPECT_EQ(a.rfind(e, 17), 17);
+ EXPECT_EQ(a.rfind(g), StringPiece::npos);
+ EXPECT_EQ(d.rfind(b), StringPiece::npos);
+ EXPECT_EQ(e.rfind(b), StringPiece::npos);
+ EXPECT_EQ(d.rfind(b, 4), StringPiece::npos);
+ EXPECT_EQ(e.rfind(b, 7), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(d.rfind(d, 4), string().rfind(string()));
+ EXPECT_EQ(e.rfind(d, 7), string().rfind(string()));
+ EXPECT_EQ(d.rfind(e, 4), string().rfind(string()));
+ EXPECT_EQ(e.rfind(e, 7), string().rfind(string()));
+ EXPECT_EQ(d.rfind(d), string().rfind(string()));
+ EXPECT_EQ(e.rfind(d), string().rfind(string()));
+ EXPECT_EQ(d.rfind(e), string().rfind(string()));
+ EXPECT_EQ(e.rfind(e), string().rfind(string()));
+
+ EXPECT_EQ(g.rfind('o'), 8);
+ EXPECT_EQ(g.rfind('q'), StringPiece::npos);
+ EXPECT_EQ(g.rfind('o', 8), 8);
+ EXPECT_EQ(g.rfind('o', 7), 4);
+ EXPECT_EQ(g.rfind('o', 3), StringPiece::npos);
+ EXPECT_EQ(f.rfind('\0'), 3);
+ EXPECT_EQ(f.rfind('\0', 12), 3);
+ EXPECT_EQ(f.rfind('3'), 2);
+ EXPECT_EQ(f.rfind('5'), 5);
+ // empty string nonsense
+ EXPECT_EQ(d.rfind('o'), StringPiece::npos);
+ EXPECT_EQ(e.rfind('o'), StringPiece::npos);
+ EXPECT_EQ(d.rfind('o', 4), StringPiece::npos);
+ EXPECT_EQ(e.rfind('o', 7), StringPiece::npos);
+
+ EXPECT_EQ(a.find_first_of(b), 0);
+ EXPECT_EQ(a.find_first_of(b, 0), 0);
+ EXPECT_EQ(a.find_first_of(b, 1), 1);
+ EXPECT_EQ(a.find_first_of(b, 2), 2);
+ EXPECT_EQ(a.find_first_of(b, 3), StringPiece::npos);
+ EXPECT_EQ(a.find_first_of(c), 23);
+ EXPECT_EQ(a.find_first_of(c, 23), 23);
+ EXPECT_EQ(a.find_first_of(c, 24), 24);
+ EXPECT_EQ(a.find_first_of(c, 25), 25);
+ EXPECT_EQ(a.find_first_of(c, 26), StringPiece::npos);
+ EXPECT_EQ(g.find_first_of(b), 13);
+ EXPECT_EQ(g.find_first_of(c), 0);
+ EXPECT_EQ(a.find_first_of(f), StringPiece::npos);
+ EXPECT_EQ(f.find_first_of(a), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(a.find_first_of(d), StringPiece::npos);
+ EXPECT_EQ(a.find_first_of(e), StringPiece::npos);
+ EXPECT_EQ(d.find_first_of(b), StringPiece::npos);
+ EXPECT_EQ(e.find_first_of(b), StringPiece::npos);
+ EXPECT_EQ(d.find_first_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_first_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_first_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_first_of(e), StringPiece::npos);
+
+ EXPECT_EQ(a.find_first_not_of(b), 3);
+ EXPECT_EQ(a.find_first_not_of(c), 0);
+ EXPECT_EQ(b.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(c.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(f.find_first_not_of(a), 0);
+ EXPECT_EQ(a.find_first_not_of(f), 0);
+ EXPECT_EQ(a.find_first_not_of(d), 0);
+ EXPECT_EQ(a.find_first_not_of(e), 0);
+ // empty string nonsense
+ EXPECT_EQ(d.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(d.find_first_not_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_first_not_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of(e), StringPiece::npos);
+
+ StringPiece h("====");
+ EXPECT_EQ(h.find_first_not_of('='), StringPiece::npos);
+ EXPECT_EQ(h.find_first_not_of('=', 3), StringPiece::npos);
+ EXPECT_EQ(h.find_first_not_of('\0'), 0);
+ EXPECT_EQ(g.find_first_not_of('x'), 2);
+ EXPECT_EQ(f.find_first_not_of('\0'), 0);
+ EXPECT_EQ(f.find_first_not_of('\0', 3), 4);
+ EXPECT_EQ(f.find_first_not_of('\0', 2), 2);
+ // empty string nonsense
+ EXPECT_EQ(d.find_first_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(d.find_first_not_of('\0'), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of('\0'), StringPiece::npos);
+
+ // StringPiece g("xx not found bb");
+ StringPiece i("56");
+ EXPECT_EQ(h.find_last_of(a), StringPiece::npos);
+ EXPECT_EQ(g.find_last_of(a), g.size()-1);
+ EXPECT_EQ(a.find_last_of(b), 2);
+ EXPECT_EQ(a.find_last_of(c), a.size()-1);
+ EXPECT_EQ(f.find_last_of(i), 6);
+ EXPECT_EQ(a.find_last_of('a'), 0);
+ EXPECT_EQ(a.find_last_of('b'), 1);
+ EXPECT_EQ(a.find_last_of('z'), 25);
+ EXPECT_EQ(a.find_last_of('a', 5), 0);
+ EXPECT_EQ(a.find_last_of('b', 5), 1);
+ EXPECT_EQ(a.find_last_of('b', 0), StringPiece::npos);
+ EXPECT_EQ(a.find_last_of('z', 25), 25);
+ EXPECT_EQ(a.find_last_of('z', 24), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(i, 5), 5);
+ EXPECT_EQ(f.find_last_of(i, 6), 6);
+ EXPECT_EQ(f.find_last_of(a, 4), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(f.find_last_of(d), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(e), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(e), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(f), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(f), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(f, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(f, 4), StringPiece::npos);
+
+ EXPECT_EQ(a.find_last_not_of(b), a.size()-1);
+ EXPECT_EQ(a.find_last_not_of(c), 22);
+ EXPECT_EQ(b.find_last_not_of(a), StringPiece::npos);
+ EXPECT_EQ(b.find_last_not_of(b), StringPiece::npos);
+ EXPECT_EQ(f.find_last_not_of(i), 4);
+ EXPECT_EQ(a.find_last_not_of(c, 24), 22);
+ EXPECT_EQ(a.find_last_not_of(b, 3), 3);
+ EXPECT_EQ(a.find_last_not_of(b, 2), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(f.find_last_not_of(d), f.size()-1);
+ EXPECT_EQ(f.find_last_not_of(e), f.size()-1);
+ EXPECT_EQ(f.find_last_not_of(d, 4), 4);
+ EXPECT_EQ(f.find_last_not_of(e, 4), 4);
+ EXPECT_EQ(d.find_last_not_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(e), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(f), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(f), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(f, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(f, 4), StringPiece::npos);
+
+ EXPECT_EQ(h.find_last_not_of('x'), h.size() - 1);
+ EXPECT_EQ(h.find_last_not_of('='), StringPiece::npos);
+ EXPECT_EQ(b.find_last_not_of('c'), 1);
+ EXPECT_EQ(h.find_last_not_of('x', 2), 2);
+ EXPECT_EQ(h.find_last_not_of('=', 2), StringPiece::npos);
+ EXPECT_EQ(b.find_last_not_of('b', 1), 0);
+ // empty string nonsense
+ EXPECT_EQ(d.find_last_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of('\0'), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of('\0'), StringPiece::npos);
+
+ EXPECT_EQ(a.substr(0, 3), b);
+ EXPECT_EQ(a.substr(23), c);
+ EXPECT_EQ(a.substr(23, 3), c);
+ EXPECT_EQ(a.substr(23, 99), c);
+ EXPECT_EQ(a.substr(0), a);
+ EXPECT_EQ(a.substr(3, 2), "de");
+ // empty string nonsense
+ EXPECT_EQ(a.substr(99, 2), e);
+ EXPECT_EQ(d.substr(99), e);
+ EXPECT_EQ(d.substr(0, 99), e);
+ EXPECT_EQ(d.substr(99, 99), e);
+ // use of npos
+ EXPECT_EQ(a.substr(0, StringPiece::npos), a);
+ EXPECT_EQ(a.substr(23, StringPiece::npos), c);
+ EXPECT_EQ(a.substr(StringPiece::npos, 0), e);
+ EXPECT_EQ(a.substr(StringPiece::npos, 1), e);
+ EXPECT_EQ(a.substr(StringPiece::npos, StringPiece::npos), e);
+
+ // Substring constructors.
+ EXPECT_EQ(StringPiece(a, 0, 3), b);
+ EXPECT_EQ(StringPiece(a, 23), c);
+ EXPECT_EQ(StringPiece(a, 23, 3), c);
+ EXPECT_EQ(StringPiece(a, 23, 99), c);
+ EXPECT_EQ(StringPiece(a, 0), a);
+ EXPECT_EQ(StringPiece(a, 3, 2), "de");
+ // empty string nonsense
+ EXPECT_EQ(StringPiece(d, 0, 99), e);
+ // Verify that they work taking an actual string, not just a StringPiece.
+ string a2 = a.as_string();
+ EXPECT_EQ(StringPiece(a2, 0, 3), b);
+ EXPECT_EQ(StringPiece(a2, 23), c);
+ EXPECT_EQ(StringPiece(a2, 23, 3), c);
+ EXPECT_EQ(StringPiece(a2, 23, 99), c);
+ EXPECT_EQ(StringPiece(a2, 0), a);
+ EXPECT_EQ(StringPiece(a2, 3, 2), "de");
+}
+
+TEST(StringPiece, Custom) {
+ StringPiece a("foobar");
+ string s1("123");
+ s1 += '\0';
+ s1 += "456";
+ StringPiece b(s1);
+ StringPiece e;
+ string s2;
+
+ // CopyToString
+ a.CopyToString(&s2);
+ EXPECT_EQ(s2.size(), 6);
+ EXPECT_EQ(s2, "foobar");
+ b.CopyToString(&s2);
+ EXPECT_EQ(s2.size(), 7);
+ EXPECT_EQ(s1, s2);
+ e.CopyToString(&s2);
+ EXPECT_TRUE(s2.empty());
+
+ // AppendToString
+ s2.erase();
+ a.AppendToString(&s2);
+ EXPECT_EQ(s2.size(), 6);
+ EXPECT_EQ(s2, "foobar");
+ a.AppendToString(&s2);
+ EXPECT_EQ(s2.size(), 12);
+ EXPECT_EQ(s2, "foobarfoobar");
+
+ // starts_with
+ EXPECT_TRUE(a.starts_with(a));
+ EXPECT_TRUE(a.starts_with("foo"));
+ EXPECT_TRUE(a.starts_with(e));
+ EXPECT_TRUE(b.starts_with(s1));
+ EXPECT_TRUE(b.starts_with(b));
+ EXPECT_TRUE(b.starts_with(e));
+ EXPECT_TRUE(e.starts_with(""));
+ EXPECT_TRUE(!a.starts_with(b));
+ EXPECT_TRUE(!b.starts_with(a));
+ EXPECT_TRUE(!e.starts_with(a));
+
+ // ends with
+ EXPECT_TRUE(a.ends_with(a));
+ EXPECT_TRUE(a.ends_with("bar"));
+ EXPECT_TRUE(a.ends_with(e));
+ EXPECT_TRUE(b.ends_with(s1));
+ EXPECT_TRUE(b.ends_with(b));
+ EXPECT_TRUE(b.ends_with(e));
+ EXPECT_TRUE(e.ends_with(""));
+ EXPECT_TRUE(!a.ends_with(b));
+ EXPECT_TRUE(!b.ends_with(a));
+ EXPECT_TRUE(!e.ends_with(a));
+
+ // remove_prefix
+ StringPiece c(a);
+ c.remove_prefix(3);
+ EXPECT_EQ(c, "bar");
+ c = a;
+ c.remove_prefix(0);
+ EXPECT_EQ(c, a);
+ c.remove_prefix(c.size());
+ EXPECT_EQ(c, e);
+
+ // remove_suffix
+ c = a;
+ c.remove_suffix(3);
+ EXPECT_EQ(c, "foo");
+ c = a;
+ c.remove_suffix(0);
+ EXPECT_EQ(c, a);
+ c.remove_suffix(c.size());
+ EXPECT_EQ(c, e);
+
+ // set
+ c.set("foobar", 6);
+ EXPECT_EQ(c, a);
+ c.set("foobar", 0);
+ EXPECT_EQ(c, e);
+ c.set("foobar", 7);
+ EXPECT_NE(c, a);
+
+ c.set("foobar");
+ EXPECT_EQ(c, a);
+
+ c.set(static_cast<const void*>("foobar"), 6);
+ EXPECT_EQ(c, a);
+ c.set(static_cast<const void*>("foobar"), 0);
+ EXPECT_EQ(c, e);
+ c.set(static_cast<const void*>("foobar"), 7);
+ EXPECT_NE(c, a);
+
+ // as_string
+ string s3(a.as_string().c_str(), 7);
+ EXPECT_EQ(c, s3);
+ string s4(e.as_string());
+ EXPECT_TRUE(s4.empty());
+
+ // ToString
+ {
+ string s5(a.ToString().c_str(), 7);
+ EXPECT_EQ(c, s5);
+ string s6(e.ToString());
+ EXPECT_TRUE(s6.empty());
+ }
+
+ // Consume
+ a.set("foobar");
+ EXPECT_TRUE(a.Consume("foo"));
+ EXPECT_EQ(a, "bar");
+ EXPECT_FALSE(a.Consume("foo"));
+ EXPECT_FALSE(a.Consume("barbar"));
+ EXPECT_FALSE(a.Consume("ar"));
+ EXPECT_EQ(a, "bar");
+
+ a.set("foobar");
+ EXPECT_TRUE(a.ConsumeFromEnd("bar"));
+ EXPECT_EQ(a, "foo");
+ EXPECT_FALSE(a.ConsumeFromEnd("bar"));
+ EXPECT_FALSE(a.ConsumeFromEnd("foofoo"));
+ EXPECT_FALSE(a.ConsumeFromEnd("fo"));
+ EXPECT_EQ(a, "foo");
+}
+
+TEST(StringPiece, Contains) {
+ StringPiece a("abcdefg");
+ StringPiece b("abcd");
+ StringPiece c("efg");
+ StringPiece d("gh");
+ EXPECT_TRUE(a.contains(b));
+ EXPECT_TRUE(a.contains(c));
+ EXPECT_TRUE(!a.contains(d));
+}
+
+TEST(StringPiece, NULLInput) {
+ // we used to crash here, but now we don't.
+ StringPiece s(NULL);
+ EXPECT_EQ(s.data(), (const char*)NULL);
+ EXPECT_EQ(s.size(), 0);
+
+ s.set(NULL);
+ EXPECT_EQ(s.data(), (const char*)NULL);
+ EXPECT_EQ(s.size(), 0);
+
+ // .ToString() on a StringPiece with NULL should produce the empty string.
+ EXPECT_EQ("", s.ToString());
+ EXPECT_EQ("", s.as_string());
+}
+
+TEST(StringPiece, Comparisons2) {
+ StringPiece abc("abcdefghijklmnopqrstuvwxyz");
+
+ // check comparison operations on strings longer than 4 bytes.
+ EXPECT_EQ(abc, StringPiece("abcdefghijklmnopqrstuvwxyz"));
+ EXPECT_EQ(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyz")), 0);
+
+ EXPECT_LT(abc, StringPiece("abcdefghijklmnopqrstuvwxzz"));
+ EXPECT_LT(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxzz")), 0);
+
+ EXPECT_GT(abc, StringPiece("abcdefghijklmnopqrstuvwxyy"));
+ EXPECT_GT(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyy")), 0);
+
+ // starts_with
+ EXPECT_TRUE(abc.starts_with(abc));
+ EXPECT_TRUE(abc.starts_with("abcdefghijklm"));
+ EXPECT_TRUE(!abc.starts_with("abcdefguvwxyz"));
+
+ // ends_with
+ EXPECT_TRUE(abc.ends_with(abc));
+ EXPECT_TRUE(!abc.ends_with("abcdefguvwxyz"));
+ EXPECT_TRUE(abc.ends_with("nopqrstuvwxyz"));
+}
+
+TEST(ComparisonOpsTest, StringCompareNotAmbiguous) {
+ EXPECT_EQ("hello", string("hello"));
+ EXPECT_LT("hello", string("world"));
+}
+
+TEST(ComparisonOpsTest, HeterogenousStringPieceEquals) {
+ EXPECT_EQ(StringPiece("hello"), string("hello"));
+ EXPECT_EQ("hello", StringPiece("hello"));
+}
+
+TEST(FindOneCharTest, EdgeCases) {
+ StringPiece a("xxyyyxx");
+
+ // Set a = "xyyyx".
+ a.remove_prefix(1);
+ a.remove_suffix(1);
+
+ EXPECT_EQ(0, a.find('x'));
+ EXPECT_EQ(0, a.find('x', 0));
+ EXPECT_EQ(4, a.find('x', 1));
+ EXPECT_EQ(4, a.find('x', 4));
+ EXPECT_EQ(StringPiece::npos, a.find('x', 5));
+
+ EXPECT_EQ(4, a.rfind('x'));
+ EXPECT_EQ(4, a.rfind('x', 5));
+ EXPECT_EQ(4, a.rfind('x', 4));
+ EXPECT_EQ(0, a.rfind('x', 3));
+ EXPECT_EQ(0, a.rfind('x', 0));
+
+ // Set a = "yyy".
+ a.remove_prefix(1);
+ a.remove_suffix(1);
+
+ EXPECT_EQ(StringPiece::npos, a.find('x'));
+ EXPECT_EQ(StringPiece::npos, a.rfind('x'));
+}
+
+#ifndef NDEBUG
+TEST(NonNegativeLenTest, NonNegativeLen) {
+ EXPECT_DEATH(StringPiece("xyz", -1), "len >= 0");
+}
+#endif // ndef DEBUG
+
+} // namespace
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc
index 7ecc17ee..99e8bf1d 100644
--- a/src/google/protobuf/stubs/strutil.cc
+++ b/src/google/protobuf/stubs/strutil.cc
@@ -31,6 +31,7 @@
// from google3/strings/strutil.cc
#include <google/protobuf/stubs/strutil.h>
+
#include <errno.h>
#include <float.h> // FLT_DIG and DBL_DIG
#include <limits>
@@ -38,6 +39,8 @@
#include <stdio.h>
#include <iterator>
+#include <google/protobuf/stubs/stl_util.h>
+
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
//
@@ -309,17 +312,6 @@ void JoinStrings(const vector<string>& components,
#define IS_OCTAL_DIGIT(c) (((c) >= '0') && ((c) <= '7'))
-inline int hex_digit_to_int(char c) {
- /* Assume ASCII. */
- assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61);
- assert(isxdigit(c));
- int x = static_cast<unsigned char>(c);
- if (x > '9') {
- x += 9;
- }
- return x & 0xf;
-}
-
// Protocol buffers doesn't ever care about errors, but I don't want to remove
// the code.
#define LOG_STRING(LEVEL, VECTOR) GOOGLE_LOG_IF(LEVEL, false)
@@ -652,14 +644,15 @@ inline bool safe_parse_sign(string* text /*inout*/,
return true;
}
-inline bool safe_parse_positive_int(
- string text, int32* value_p) {
+template<typename IntType>
+bool safe_parse_positive_int(
+ string text, IntType* value_p) {
int base = 10;
- int32 value = 0;
- const int32 vmax = std::numeric_limits<int32>::max();
+ IntType value = 0;
+ const IntType vmax = std::numeric_limits<IntType>::max();
assert(vmax > 0);
assert(vmax >= base);
- const int32 vmax_over_base = vmax / base;
+ const IntType vmax_over_base = vmax / base;
const char* start = text.data();
const char* end = start + text.size();
// loop over digits
@@ -685,14 +678,15 @@ inline bool safe_parse_positive_int(
return true;
}
-inline bool safe_parse_negative_int(
- string text, int32* value_p) {
+template<typename IntType>
+bool safe_parse_negative_int(
+ const string& text, IntType* value_p) {
int base = 10;
- int32 value = 0;
- const int32 vmin = std::numeric_limits<int32>::min();
+ IntType value = 0;
+ const IntType vmin = std::numeric_limits<IntType>::min();
assert(vmin < 0);
assert(vmin <= 0 - base);
- int32 vmin_over_base = vmin / base;
+ IntType vmin_over_base = vmin / base;
// 2003 c++ standard [expr.mul]
// "... the sign of the remainder is implementation-defined."
// Although (vmin/base)*base + vmin%base is always vmin.
@@ -725,7 +719,8 @@ inline bool safe_parse_negative_int(
return true;
}
-bool safe_int(string text, int32* value_p) {
+template<typename IntType>
+bool safe_int_internal(string text, IntType* value_p) {
*value_p = 0;
bool negative;
if (!safe_parse_sign(&text, &negative)) {
@@ -738,6 +733,16 @@ bool safe_int(string text, int32* value_p) {
}
}
+template<typename IntType>
+bool safe_uint_internal(string text, IntType* value_p) {
+ *value_p = 0;
+ bool negative;
+ if (!safe_parse_sign(&text, &negative) || negative) {
+ return false;
+ }
+ return safe_parse_positive_int(text, value_p);
+}
+
// ----------------------------------------------------------------------
// FastIntToBuffer()
// FastInt64ToBuffer()
@@ -1236,6 +1241,41 @@ char* DoubleToBuffer(double value, char* buffer) {
return buffer;
}
+static int memcasecmp(const char *s1, const char *s2, size_t len) {
+ const unsigned char *us1 = reinterpret_cast<const unsigned char *>(s1);
+ const unsigned char *us2 = reinterpret_cast<const unsigned char *>(s2);
+
+ for ( int i = 0; i < len; i++ ) {
+ const int diff =
+ static_cast<int>(static_cast<unsigned char>(ascii_tolower(us1[i]))) -
+ static_cast<int>(static_cast<unsigned char>(ascii_tolower(us2[i])));
+ if (diff != 0) return diff;
+ }
+ return 0;
+}
+
+inline bool CaseEqual(StringPiece s1, StringPiece s2) {
+ if (s1.size() != s2.size()) return false;
+ return memcasecmp(s1.data(), s2.data(), s1.size()) == 0;
+}
+
+bool safe_strtob(StringPiece str, bool* value) {
+ GOOGLE_CHECK(value != NULL) << "NULL output boolean given.";
+ if (CaseEqual(str, "true") || CaseEqual(str, "t") ||
+ CaseEqual(str, "yes") || CaseEqual(str, "y") ||
+ CaseEqual(str, "1")) {
+ *value = true;
+ return true;
+ }
+ if (CaseEqual(str, "false") || CaseEqual(str, "f") ||
+ CaseEqual(str, "no") || CaseEqual(str, "n") ||
+ CaseEqual(str, "0")) {
+ *value = false;
+ return true;
+ }
+ return false;
+}
+
bool safe_strtof(const char* str, float* value) {
char* endptr;
errno = 0; // errno only gets set on errors
@@ -1247,6 +1287,34 @@ bool safe_strtof(const char* str, float* value) {
return *str != 0 && *endptr == 0 && errno == 0;
}
+bool safe_strtod(const char* str, double* value) {
+ char* endptr;
+ *value = strtod(str, &endptr);
+ if (endptr != str) {
+ while (ascii_isspace(*endptr)) ++endptr;
+ }
+ // Ignore range errors from strtod. The values it
+ // returns on underflow and overflow are the right
+ // fallback in a robust setting.
+ return *str != '\0' && *endptr == '\0';
+}
+
+bool safe_strto32(const string& str, int32* value) {
+ return safe_int_internal(str, value);
+}
+
+bool safe_strtou32(const string& str, uint32* value) {
+ return safe_uint_internal(str, value);
+}
+
+bool safe_strto64(const string& str, int64* value) {
+ return safe_int_internal(str, value);
+}
+
+bool safe_strtou64(const string& str, uint64* value) {
+ return safe_uint_internal(str, value);
+}
+
char* FloatToBuffer(float value, char* buffer) {
// FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
// platforms these days. Just in case some system exists where FLT_DIG
@@ -1518,5 +1586,661 @@ int GlobalReplaceSubstring(const string& substring,
return num_replacements;
}
+int CalculateBase64EscapedLen(int input_len, bool do_padding) {
+ // Base64 encodes three bytes of input at a time. If the input is not
+ // divisible by three, we pad as appropriate.
+ //
+ // (from http://tools.ietf.org/html/rfc3548)
+ // Special processing is performed if fewer than 24 bits are available
+ // at the end of the data being encoded. A full encoding quantum is
+ // always completed at the end of a quantity. When fewer than 24 input
+ // bits are available in an input group, zero bits are added (on the
+ // right) to form an integral number of 6-bit groups. Padding at the
+ // end of the data is performed using the '=' character. Since all base
+ // 64 input is an integral number of octets, only the following cases
+ // can arise:
+
+
+ // Base64 encodes each three bytes of input into four bytes of output.
+ int len = (input_len / 3) * 4;
+
+ if (input_len % 3 == 0) {
+ // (from http://tools.ietf.org/html/rfc3548)
+ // (1) the final quantum of encoding input is an integral multiple of 24
+ // bits; here, the final unit of encoded output will be an integral
+ // multiple of 4 characters with no "=" padding,
+ } else if (input_len % 3 == 1) {
+ // (from http://tools.ietf.org/html/rfc3548)
+ // (2) the final quantum of encoding input is exactly 8 bits; here, the
+ // final unit of encoded output will be two characters followed by two
+ // "=" padding characters, or
+ len += 2;
+ if (do_padding) {
+ len += 2;
+ }
+ } else { // (input_len % 3 == 2)
+ // (from http://tools.ietf.org/html/rfc3548)
+ // (3) the final quantum of encoding input is exactly 16 bits; here, the
+ // final unit of encoded output will be three characters followed by one
+ // "=" padding character.
+ len += 3;
+ if (do_padding) {
+ len += 1;
+ }
+ }
+
+ assert(len >= input_len); // make sure we didn't overflow
+ return len;
+}
+
+// Base64Escape does padding, so this calculation includes padding.
+int CalculateBase64EscapedLen(int input_len) {
+ return CalculateBase64EscapedLen(input_len, true);
+}
+
+// ----------------------------------------------------------------------
+// int Base64Unescape() - base64 decoder
+// int Base64Escape() - base64 encoder
+// int WebSafeBase64Unescape() - Google's variation of base64 decoder
+// int WebSafeBase64Escape() - Google's variation of base64 encoder
+//
+// Check out
+// http://tools.ietf.org/html/rfc2045 for formal description, but what we
+// care about is that...
+// Take the encoded stuff in groups of 4 characters and turn each
+// character into a code 0 to 63 thus:
+// A-Z map to 0 to 25
+// a-z map to 26 to 51
+// 0-9 map to 52 to 61
+// +(- for WebSafe) maps to 62
+// /(_ for WebSafe) maps to 63
+// There will be four numbers, all less than 64 which can be represented
+// by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
+// Arrange the 6 digit binary numbers into three bytes as such:
+// aaaaaabb bbbbcccc ccdddddd
+// Equals signs (one or two) are used at the end of the encoded block to
+// indicate that the text was not an integer multiple of three bytes long.
+// ----------------------------------------------------------------------
+
+int Base64UnescapeInternal(const char *src_param, int szsrc,
+ char *dest, int szdest,
+ const signed char* unbase64) {
+ static const char kPad64Equals = '=';
+ static const char kPad64Dot = '.';
+
+ int decode = 0;
+ int destidx = 0;
+ int state = 0;
+ unsigned int ch = 0;
+ unsigned int temp = 0;
+
+ // If "char" is signed by default, using *src as an array index results in
+ // accessing negative array elements. Treat the input as a pointer to
+ // unsigned char to avoid this.
+ const unsigned char *src = reinterpret_cast<const unsigned char*>(src_param);
+
+ // The GET_INPUT macro gets the next input character, skipping
+ // over any whitespace, and stopping when we reach the end of the
+ // string or when we read any non-data character. The arguments are
+ // an arbitrary identifier (used as a label for goto) and the number
+ // of data bytes that must remain in the input to avoid aborting the
+ // loop.
+#define GET_INPUT(label, remain) \
+ label: \
+ --szsrc; \
+ ch = *src++; \
+ decode = unbase64[ch]; \
+ if (decode < 0) { \
+ if (ascii_isspace(ch) && szsrc >= remain) \
+ goto label; \
+ state = 4 - remain; \
+ break; \
+ }
+
+ // if dest is null, we're just checking to see if it's legal input
+ // rather than producing output. (I suspect this could just be done
+ // with a regexp...). We duplicate the loop so this test can be
+ // outside it instead of in every iteration.
+
+ if (dest) {
+ // This loop consumes 4 input bytes and produces 3 output bytes
+ // per iteration. We can't know at the start that there is enough
+ // data left in the string for a full iteration, so the loop may
+ // break out in the middle; if so 'state' will be set to the
+ // number of input bytes read.
+
+ while (szsrc >= 4) {
+ // We'll start by optimistically assuming that the next four
+ // bytes of the string (src[0..3]) are four good data bytes
+ // (that is, no nulls, whitespace, padding chars, or illegal
+ // chars). We need to test src[0..2] for nulls individually
+ // before constructing temp to preserve the property that we
+ // never read past a null in the string (no matter how long
+ // szsrc claims the string is).
+
+ if (!src[0] || !src[1] || !src[2] ||
+ (temp = ((unsigned(unbase64[src[0]]) << 18) |
+ (unsigned(unbase64[src[1]]) << 12) |
+ (unsigned(unbase64[src[2]]) << 6) |
+ (unsigned(unbase64[src[3]])))) & 0x80000000) {
+ // Iff any of those four characters was bad (null, illegal,
+ // whitespace, padding), then temp's high bit will be set
+ // (because unbase64[] is -1 for all bad characters).
+ //
+ // We'll back up and resort to the slower decoder, which knows
+ // how to handle those cases.
+
+ GET_INPUT(first, 4);
+ temp = decode;
+ GET_INPUT(second, 3);
+ temp = (temp << 6) | decode;
+ GET_INPUT(third, 2);
+ temp = (temp << 6) | decode;
+ GET_INPUT(fourth, 1);
+ temp = (temp << 6) | decode;
+ } else {
+ // We really did have four good data bytes, so advance four
+ // characters in the string.
+
+ szsrc -= 4;
+ src += 4;
+ decode = -1;
+ ch = '\0';
+ }
+
+ // temp has 24 bits of input, so write that out as three bytes.
+
+ if (destidx+3 > szdest) return -1;
+ dest[destidx+2] = temp;
+ temp >>= 8;
+ dest[destidx+1] = temp;
+ temp >>= 8;
+ dest[destidx] = temp;
+ destidx += 3;
+ }
+ } else {
+ while (szsrc >= 4) {
+ if (!src[0] || !src[1] || !src[2] ||
+ (temp = ((unsigned(unbase64[src[0]]) << 18) |
+ (unsigned(unbase64[src[1]]) << 12) |
+ (unsigned(unbase64[src[2]]) << 6) |
+ (unsigned(unbase64[src[3]])))) & 0x80000000) {
+ GET_INPUT(first_no_dest, 4);
+ GET_INPUT(second_no_dest, 3);
+ GET_INPUT(third_no_dest, 2);
+ GET_INPUT(fourth_no_dest, 1);
+ } else {
+ szsrc -= 4;
+ src += 4;
+ decode = -1;
+ ch = '\0';
+ }
+ destidx += 3;
+ }
+ }
+
+#undef GET_INPUT
+
+ // if the loop terminated because we read a bad character, return
+ // now.
+ if (decode < 0 && ch != '\0' &&
+ ch != kPad64Equals && ch != kPad64Dot && !ascii_isspace(ch))
+ return -1;
+
+ if (ch == kPad64Equals || ch == kPad64Dot) {
+ // if we stopped by hitting an '=' or '.', un-read that character -- we'll
+ // look at it again when we count to check for the proper number of
+ // equals signs at the end.
+ ++szsrc;
+ --src;
+ } else {
+ // This loop consumes 1 input byte per iteration. It's used to
+ // clean up the 0-3 input bytes remaining when the first, faster
+ // loop finishes. 'temp' contains the data from 'state' input
+ // characters read by the first loop.
+ while (szsrc > 0) {
+ --szsrc;
+ ch = *src++;
+ decode = unbase64[ch];
+ if (decode < 0) {
+ if (ascii_isspace(ch)) {
+ continue;
+ } else if (ch == '\0') {
+ break;
+ } else if (ch == kPad64Equals || ch == kPad64Dot) {
+ // back up one character; we'll read it again when we check
+ // for the correct number of pad characters at the end.
+ ++szsrc;
+ --src;
+ break;
+ } else {
+ return -1;
+ }
+ }
+
+ // Each input character gives us six bits of output.
+ temp = (temp << 6) | decode;
+ ++state;
+ if (state == 4) {
+ // If we've accumulated 24 bits of output, write that out as
+ // three bytes.
+ if (dest) {
+ if (destidx+3 > szdest) return -1;
+ dest[destidx+2] = temp;
+ temp >>= 8;
+ dest[destidx+1] = temp;
+ temp >>= 8;
+ dest[destidx] = temp;
+ }
+ destidx += 3;
+ state = 0;
+ temp = 0;
+ }
+ }
+ }
+
+ // Process the leftover data contained in 'temp' at the end of the input.
+ int expected_equals = 0;
+ switch (state) {
+ case 0:
+ // Nothing left over; output is a multiple of 3 bytes.
+ break;
+
+ case 1:
+ // Bad input; we have 6 bits left over.
+ return -1;
+
+ case 2:
+ // Produce one more output byte from the 12 input bits we have left.
+ if (dest) {
+ if (destidx+1 > szdest) return -1;
+ temp >>= 4;
+ dest[destidx] = temp;
+ }
+ ++destidx;
+ expected_equals = 2;
+ break;
+
+ case 3:
+ // Produce two more output bytes from the 18 input bits we have left.
+ if (dest) {
+ if (destidx+2 > szdest) return -1;
+ temp >>= 2;
+ dest[destidx+1] = temp;
+ temp >>= 8;
+ dest[destidx] = temp;
+ }
+ destidx += 2;
+ expected_equals = 1;
+ break;
+
+ default:
+ // state should have no other values at this point.
+ GOOGLE_LOG(FATAL) << "This can't happen; base64 decoder state = " << state;
+ }
+
+ // The remainder of the string should be all whitespace, mixed with
+ // exactly 0 equals signs, or exactly 'expected_equals' equals
+ // signs. (Always accepting 0 equals signs is a google extension
+ // not covered in the RFC, as is accepting dot as the pad character.)
+
+ int equals = 0;
+ while (szsrc > 0 && *src) {
+ if (*src == kPad64Equals || *src == kPad64Dot)
+ ++equals;
+ else if (!ascii_isspace(*src))
+ return -1;
+ --szsrc;
+ ++src;
+ }
+
+ return (equals == 0 || equals == expected_equals) ? destidx : -1;
+}
+
+// The arrays below were generated by the following code
+// #include <sys/time.h>
+// #include <stdlib.h>
+// #include <string.h>
+// main()
+// {
+// static const char Base64[] =
+// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+// char *pos;
+// int idx, i, j;
+// printf(" ");
+// for (i = 0; i < 255; i += 8) {
+// for (j = i; j < i + 8; j++) {
+// pos = strchr(Base64, j);
+// if ((pos == NULL) || (j == 0))
+// idx = -1;
+// else
+// idx = pos - Base64;
+// if (idx == -1)
+// printf(" %2d, ", idx);
+// else
+// printf(" %2d/*%c*/,", idx, j);
+// }
+// printf("\n ");
+// }
+// }
+//
+// where the value of "Base64[]" was replaced by one of the base-64 conversion
+// tables from the functions below.
+static const signed char kUnBase64[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62/*+*/, -1, -1, -1, 63/*/ */,
+ 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+ 60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
+ -1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
+ 07/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+ 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+ 23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, -1,
+ -1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+ 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+ 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+ 49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+static const signed char kUnWebSafeBase64[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 62/*-*/, -1, -1,
+ 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+ 60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
+ -1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
+ 07/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+ 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+ 23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, 63/*_*/,
+ -1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+ 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+ 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+ 49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) {
+ return Base64UnescapeInternal(src, szsrc, dest, szdest, kUnWebSafeBase64);
+}
+
+static bool Base64UnescapeInternal(const char* src, int slen, string* dest,
+ const signed char* unbase64) {
+ // Determine the size of the output string. Base64 encodes every 3 bytes into
+ // 4 characters. any leftover chars are added directly for good measure.
+ // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548
+ const int dest_len = 3 * (slen / 4) + (slen % 4);
+
+ dest->resize(dest_len);
+
+ // We are getting the destination buffer by getting the beginning of the
+ // string and converting it into a char *.
+ const int len = Base64UnescapeInternal(src, slen, string_as_array(dest),
+ dest_len, unbase64);
+ if (len < 0) {
+ dest->clear();
+ return false;
+ }
+
+ // could be shorter if there was padding
+ GOOGLE_DCHECK_LE(len, dest_len);
+ dest->erase(len);
+
+ return true;
+}
+
+bool Base64Unescape(StringPiece src, string* dest) {
+ return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64);
+}
+
+bool WebSafeBase64Unescape(StringPiece src, string* dest) {
+ return Base64UnescapeInternal(src.data(), src.size(), dest, kUnWebSafeBase64);
+}
+
+int Base64EscapeInternal(const unsigned char *src, int szsrc,
+ char *dest, int szdest, const char *base64,
+ bool do_padding) {
+ static const char kPad64 = '=';
+
+ if (szsrc <= 0) return 0;
+
+ if (szsrc * 4 > szdest * 3) return 0;
+
+ char *cur_dest = dest;
+ const unsigned char *cur_src = src;
+
+ char *limit_dest = dest + szdest;
+ const unsigned char *limit_src = src + szsrc;
+
+ // Three bytes of data encodes to four characters of cyphertext.
+ // So we can pump through three-byte chunks atomically.
+ while (cur_src < limit_src - 3) { // keep going as long as we have >= 32 bits
+ uint32 in = BigEndian::Load32(cur_src) >> 8;
+
+ cur_dest[0] = base64[in >> 18];
+ in &= 0x3FFFF;
+ cur_dest[1] = base64[in >> 12];
+ in &= 0xFFF;
+ cur_dest[2] = base64[in >> 6];
+ in &= 0x3F;
+ cur_dest[3] = base64[in];
+
+ cur_dest += 4;
+ cur_src += 3;
+ }
+ // To save time, we didn't update szdest or szsrc in the loop. So do it now.
+ szdest = limit_dest - cur_dest;
+ szsrc = limit_src - cur_src;
+
+ /* now deal with the tail (<=3 bytes) */
+ switch (szsrc) {
+ case 0:
+ // Nothing left; nothing more to do.
+ break;
+ case 1: {
+ // One byte left: this encodes to two characters, and (optionally)
+ // two pad characters to round out the four-character cypherblock.
+ if ((szdest -= 2) < 0) return 0;
+ uint32 in = cur_src[0];
+ cur_dest[0] = base64[in >> 2];
+ in &= 0x3;
+ cur_dest[1] = base64[in << 4];
+ cur_dest += 2;
+ if (do_padding) {
+ if ((szdest -= 2) < 0) return 0;
+ cur_dest[0] = kPad64;
+ cur_dest[1] = kPad64;
+ cur_dest += 2;
+ }
+ break;
+ }
+ case 2: {
+ // Two bytes left: this encodes to three characters, and (optionally)
+ // one pad character to round out the four-character cypherblock.
+ if ((szdest -= 3) < 0) return 0;
+ uint32 in = BigEndian::Load16(cur_src);
+ cur_dest[0] = base64[in >> 10];
+ in &= 0x3FF;
+ cur_dest[1] = base64[in >> 4];
+ in &= 0x00F;
+ cur_dest[2] = base64[in << 2];
+ cur_dest += 3;
+ if (do_padding) {
+ if ((szdest -= 1) < 0) return 0;
+ cur_dest[0] = kPad64;
+ cur_dest += 1;
+ }
+ break;
+ }
+ case 3: {
+ // Three bytes left: same as in the big loop above. We can't do this in
+ // the loop because the loop above always reads 4 bytes, and the fourth
+ // byte is past the end of the input.
+ if ((szdest -= 4) < 0) return 0;
+ uint32 in = (cur_src[0] << 16) + BigEndian::Load16(cur_src + 1);
+ cur_dest[0] = base64[in >> 18];
+ in &= 0x3FFFF;
+ cur_dest[1] = base64[in >> 12];
+ in &= 0xFFF;
+ cur_dest[2] = base64[in >> 6];
+ in &= 0x3F;
+ cur_dest[3] = base64[in];
+ cur_dest += 4;
+ break;
+ }
+ default:
+ // Should not be reached: blocks of 4 bytes are handled
+ // in the while loop before this switch statement.
+ GOOGLE_LOG(FATAL) << "Logic problem? szsrc = " << szsrc;
+ break;
+ }
+ return (cur_dest - dest);
+}
+
+static const char kBase64Chars[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const char kWebSafeBase64Chars[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+int Base64Escape(const unsigned char *src, int szsrc, char *dest, int szdest) {
+ return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true);
+}
+int WebSafeBase64Escape(const unsigned char *src, int szsrc, char *dest,
+ int szdest, bool do_padding) {
+ return Base64EscapeInternal(src, szsrc, dest, szdest,
+ kWebSafeBase64Chars, do_padding);
+}
+
+void Base64EscapeInternal(const unsigned char* src, int szsrc,
+ string* dest, bool do_padding,
+ const char* base64_chars) {
+ const int calc_escaped_size =
+ CalculateBase64EscapedLen(szsrc, do_padding);
+ dest->resize(calc_escaped_size);
+ const int escaped_len = Base64EscapeInternal(src, szsrc,
+ string_as_array(dest),
+ dest->size(),
+ base64_chars,
+ do_padding);
+ GOOGLE_DCHECK_EQ(calc_escaped_size, escaped_len);
+ dest->erase(escaped_len);
+}
+
+void Base64Escape(const unsigned char *src, int szsrc,
+ string* dest, bool do_padding) {
+ Base64EscapeInternal(src, szsrc, dest, do_padding, kBase64Chars);
+}
+
+void WebSafeBase64Escape(const unsigned char *src, int szsrc,
+ string *dest, bool do_padding) {
+ Base64EscapeInternal(src, szsrc, dest, do_padding, kWebSafeBase64Chars);
+}
+
+void Base64Escape(StringPiece src, string* dest) {
+ Base64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+ src.size(), dest, true);
+}
+
+void WebSafeBase64Escape(StringPiece src, string* dest) {
+ WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+ src.size(), dest, false);
+}
+
+void WebSafeBase64EscapeWithPadding(StringPiece src, string* dest) {
+ WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+ src.size(), dest, true);
+}
+
+// Helper to append a Unicode code point to a string as UTF8, without bringing
+// in any external dependencies.
+int EncodeAsUTF8Char(uint32 code_point, char* output) {
+ uint32 tmp = 0;
+ int len = 0;
+ if (code_point <= 0x7f) {
+ tmp = code_point;
+ len = 1;
+ } else if (code_point <= 0x07ff) {
+ tmp = 0x0000c080 |
+ ((code_point & 0x07c0) << 2) |
+ (code_point & 0x003f);
+ len = 2;
+ } else if (code_point <= 0xffff) {
+ tmp = 0x00e08080 |
+ ((code_point & 0xf000) << 4) |
+ ((code_point & 0x0fc0) << 2) |
+ (code_point & 0x003f);
+ len = 3;
+ } else {
+ // UTF-16 is only defined for code points up to 0x10FFFF, and UTF-8 is
+ // normally only defined up to there as well.
+ tmp = 0xf0808080 |
+ ((code_point & 0x1c0000) << 6) |
+ ((code_point & 0x03f000) << 4) |
+ ((code_point & 0x000fc0) << 2) |
+ (code_point & 0x003f);
+ len = 4;
+ }
+ tmp = ghtonl(tmp);
+ memcpy(output, reinterpret_cast<const char*>(&tmp) + sizeof(tmp) - len, len);
+ return len;
+}
+
+// Table of UTF-8 character lengths, based on first byte
+static const unsigned char kUTF8LenTbl[256] = {
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4
+};
+
+// Return length of a single UTF-8 source character
+int UTF8FirstLetterNumBytes(const char* src, int len) {
+ if (len == 0) {
+ return 0;
+ }
+ return kUTF8LenTbl[*reinterpret_cast<const uint8*>(src)];
+}
+
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h
index 397122ef..b22066b6 100644
--- a/src/google/protobuf/stubs/strutil.h
+++ b/src/google/protobuf/stubs/strutil.h
@@ -36,6 +36,7 @@
#include <stdlib.h>
#include <vector>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
namespace google {
namespace protobuf {
@@ -72,7 +73,33 @@ inline bool ascii_isdigit(char c) {
}
inline bool ascii_isspace(char c) {
- return c == ' ';
+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' ||
+ c == '\r';
+}
+
+inline bool ascii_isupper(char c) {
+ return c >= 'A' && c <= 'Z';
+}
+
+inline bool ascii_islower(char c) {
+ return c >= 'a' && c <= 'z';
+}
+
+inline char ascii_toupper(char c) {
+ return ascii_islower(c) ? c - ('a' - 'A') : c;
+}
+
+inline char ascii_tolower(char c) {
+ return ascii_isupper(c) ? c + ('a' - 'A') : c;
+}
+
+inline int hex_digit_to_int(char c) {
+ /* Assume ASCII. */
+ int x = static_cast<unsigned char>(c);
+ if (x > '9') {
+ x += 9;
+ }
+ return x & 0xf;
}
// ----------------------------------------------------------------------
@@ -360,12 +387,59 @@ inline uint64 strtou64(const char *nptr, char **endptr, int base) {
}
// ----------------------------------------------------------------------
+// safe_strtob()
// safe_strto32()
-// ----------------------------------------------------------------------
-LIBPROTOBUF_EXPORT bool safe_int(string text, int32* value_p);
+// safe_strtou32()
+// safe_strto64()
+// safe_strtou64()
+// safe_strtof()
+// safe_strtod()
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT bool safe_strtob(StringPiece str, bool* value);
+
+LIBPROTOBUF_EXPORT bool safe_strto32(const string& str, int32* value);
+LIBPROTOBUF_EXPORT bool safe_strtou32(const string& str, uint32* value);
+inline bool safe_strto32(const char* str, int32* value) {
+ return safe_strto32(string(str), value);
+}
+inline bool safe_strto32(StringPiece str, int32* value) {
+ return safe_strto32(str.ToString(), value);
+}
+inline bool safe_strtou32(const char* str, uint32* value) {
+ return safe_strtou32(string(str), value);
+}
+inline bool safe_strtou32(StringPiece str, uint32* value) {
+ return safe_strtou32(str.ToString(), value);
+}
-inline bool safe_strto32(string text, int32* value) {
- return safe_int(text, value);
+LIBPROTOBUF_EXPORT bool safe_strto64(const string& str, int64* value);
+LIBPROTOBUF_EXPORT bool safe_strtou64(const string& str, uint64* value);
+inline bool safe_strto64(const char* str, int64* value) {
+ return safe_strto64(string(str), value);
+}
+inline bool safe_strto64(StringPiece str, int64* value) {
+ return safe_strto64(str.ToString(), value);
+}
+inline bool safe_strtou64(const char* str, uint64* value) {
+ return safe_strtou64(string(str), value);
+}
+inline bool safe_strtou64(StringPiece str, uint64* value) {
+ return safe_strtou64(str.ToString(), value);
+}
+
+LIBPROTOBUF_EXPORT bool safe_strtof(const char* str, float* value);
+LIBPROTOBUF_EXPORT bool safe_strtod(const char* str, double* value);
+inline bool safe_strtof(const string& str, float* value) {
+ return safe_strtof(str.c_str(), value);
+}
+inline bool safe_strtod(const string& str, double* value) {
+ return safe_strtod(str.c_str(), value);
+}
+inline bool safe_strtof(StringPiece str, float* value) {
+ return safe_strtof(str.ToString(), value);
+}
+inline bool safe_strtod(StringPiece str, double* value) {
+ return safe_strtod(str.ToString(), value);
}
// ----------------------------------------------------------------------
@@ -451,6 +525,10 @@ inline char* FastUInt64ToBuffer(uint64 i, char* buffer) {
return buffer;
}
+inline string SimpleBtoa(bool value) {
+ return value ? "true" : "false";
+}
+
// ----------------------------------------------------------------------
// SimpleItoa()
// Description: converts an integer to a string.
@@ -497,28 +575,30 @@ static const int kFloatToBufferSize = 24;
namespace strings {
+enum PadSpec {
+ NO_PAD = 1,
+ ZERO_PAD_2,
+ ZERO_PAD_3,
+ ZERO_PAD_4,
+ ZERO_PAD_5,
+ ZERO_PAD_6,
+ ZERO_PAD_7,
+ ZERO_PAD_8,
+ ZERO_PAD_9,
+ ZERO_PAD_10,
+ ZERO_PAD_11,
+ ZERO_PAD_12,
+ ZERO_PAD_13,
+ ZERO_PAD_14,
+ ZERO_PAD_15,
+ ZERO_PAD_16,
+};
+
struct Hex {
uint64 value;
- enum PadSpec {
- NONE = 1,
- ZERO_PAD_2,
- ZERO_PAD_3,
- ZERO_PAD_4,
- ZERO_PAD_5,
- ZERO_PAD_6,
- ZERO_PAD_7,
- ZERO_PAD_8,
- ZERO_PAD_9,
- ZERO_PAD_10,
- ZERO_PAD_11,
- ZERO_PAD_12,
- ZERO_PAD_13,
- ZERO_PAD_14,
- ZERO_PAD_15,
- ZERO_PAD_16,
- } spec;
+ enum PadSpec spec;
template <class Int>
- explicit Hex(Int v, PadSpec s = NONE)
+ explicit Hex(Int v, PadSpec s = NO_PAD)
: spec(s) {
// Prevent sign-extension by casting integers to
// their unsigned counterparts.
@@ -571,6 +651,9 @@ struct LIBPROTOBUF_EXPORT AlphaNum {
AlphaNum(const string& str)
: piece_data_(str.data()), piece_size_(str.size()) {}
+ AlphaNum(StringPiece str)
+ : piece_data_(str.data()), piece_size_(str.size()) {}
+
size_t size() const { return piece_size_; }
const char *data() const { return piece_data_; }
@@ -692,6 +775,12 @@ string Join(const Range& components,
}
// ----------------------------------------------------------------------
+// ToHex()
+// Return a lower-case hex string representation of the given integer.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT string ToHex(uint64 num);
+
+// ----------------------------------------------------------------------
// GlobalReplaceSubstring()
// Replaces all instances of a substring in a string. Does nothing
// if 'substring' is empty. Returns the number of replacements.
@@ -702,6 +791,83 @@ LIBPROTOBUF_EXPORT int GlobalReplaceSubstring(const string& substring,
const string& replacement,
string* s);
+// ----------------------------------------------------------------------
+// Base64Unescape()
+// Converts "src" which is encoded in Base64 to its binary equivalent and
+// writes it to "dest". If src contains invalid characters, dest is cleared
+// and the function returns false. Returns true on success.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT bool Base64Unescape(StringPiece src, string* dest);
+
+// ----------------------------------------------------------------------
+// WebSafeBase64Unescape()
+// This is a variation of Base64Unescape which uses '-' instead of '+', and
+// '_' instead of '/'. src is not null terminated, instead specify len. I
+// recommend that slen<szdest, but we honor szdest anyway.
+// RETURNS the length of dest, or -1 if src contains invalid chars.
+
+// The variation that stores into a string clears the string first, and
+// returns false (with dest empty) if src contains invalid chars; for
+// this version src and dest must be different strings.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int WebSafeBase64Unescape(const char* src, int slen,
+ char* dest, int szdest);
+LIBPROTOBUF_EXPORT bool WebSafeBase64Unescape(StringPiece src, string* dest);
+
+// Return the length to use for the output buffer given to the base64 escape
+// routines. Make sure to use the same value for do_padding in both.
+// This function may return incorrect results if given input_len values that
+// are extremely high, which should happen rarely.
+LIBPROTOBUF_EXPORT int CalculateBase64EscapedLen(int input_len,
+ bool do_padding);
+// Use this version when calling Base64Escape without a do_padding arg.
+LIBPROTOBUF_EXPORT int CalculateBase64EscapedLen(int input_len);
+
+// ----------------------------------------------------------------------
+// Base64Escape()
+// WebSafeBase64Escape()
+// Encode "src" to "dest" using base64 encoding.
+// src is not null terminated, instead specify len.
+// 'dest' should have at least CalculateBase64EscapedLen() length.
+// RETURNS the length of dest.
+// The WebSafe variation use '-' instead of '+' and '_' instead of '/'
+// so that we can place the out in the URL or cookies without having
+// to escape them. It also has an extra parameter "do_padding",
+// which when set to false will prevent padding with "=".
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int Base64Escape(const unsigned char* src, int slen,
+ char* dest, int szdest);
+LIBPROTOBUF_EXPORT int WebSafeBase64Escape(
+ const unsigned char* src, int slen, char* dest,
+ int szdest, bool do_padding);
+// Encode src into dest with padding.
+LIBPROTOBUF_EXPORT void Base64Escape(StringPiece src, string* dest);
+// Encode src into dest web-safely without padding.
+LIBPROTOBUF_EXPORT void WebSafeBase64Escape(StringPiece src, string* dest);
+// Encode src into dest web-safely with padding.
+LIBPROTOBUF_EXPORT void WebSafeBase64EscapeWithPadding(StringPiece src,
+ string* dest);
+
+LIBPROTOBUF_EXPORT void Base64Escape(const unsigned char* src, int szsrc,
+ string* dest, bool do_padding);
+LIBPROTOBUF_EXPORT void WebSafeBase64Escape(const unsigned char* src, int szsrc,
+ string* dest, bool do_padding);
+
+static const int UTFmax = 4;
+// ----------------------------------------------------------------------
+// EncodeAsUTF8Char()
+// Helper to append a Unicode code point to a string as UTF8, without bringing
+// in any external dependencies. The output buffer must be as least 4 bytes
+// large.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int EncodeAsUTF8Char(uint32 code_point, char* output);
+
+// ----------------------------------------------------------------------
+// UTF8FirstLetterNumBytes()
+// Length of the first UTF-8 character.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int UTF8FirstLetterNumBytes(const char* src, int len);
+
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/strutil_unittest.cc b/src/google/protobuf/stubs/strutil_unittest.cc
index 7f27dca0..5d62fc4a 100644
--- a/src/google/protobuf/stubs/strutil_unittest.cc
+++ b/src/google/protobuf/stubs/strutil_unittest.cc
@@ -32,9 +32,15 @@
#include <google/protobuf/stubs/strutil.h>
+#include <locale.h>
+
+#include <google/protobuf/stubs/stl_util.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
-#include <locale.h>
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
namespace google {
namespace protobuf {
@@ -68,6 +74,737 @@ TEST(StringUtilityTest, ImmuneToLocales) {
setlocale(LC_NUMERIC, old_locale.c_str());
}
+#define EXPECT_EQ_ARRAY(len, x, y, msg) \
+ for (int j = 0; j < len; ++j) { \
+ EXPECT_EQ(x[j], y[j]) << "" # x << " != " # y \
+ << " byte " << j << ": " << msg; \
+ }
+
+static struct {
+ int plain_length;
+ const char* plaintext;
+ const char* cyphertext;
+} base64_tests[] = {
+ // Empty string.
+ { 0, "", ""},
+
+ // Basic bit patterns;
+ // values obtained with "echo -n '...' | uuencode -m test"
+
+ { 1, "\000", "AA==" },
+ { 1, "\001", "AQ==" },
+ { 1, "\002", "Ag==" },
+ { 1, "\004", "BA==" },
+ { 1, "\010", "CA==" },
+ { 1, "\020", "EA==" },
+ { 1, "\040", "IA==" },
+ { 1, "\100", "QA==" },
+ { 1, "\200", "gA==" },
+
+ { 1, "\377", "/w==" },
+ { 1, "\376", "/g==" },
+ { 1, "\375", "/Q==" },
+ { 1, "\373", "+w==" },
+ { 1, "\367", "9w==" },
+ { 1, "\357", "7w==" },
+ { 1, "\337", "3w==" },
+ { 1, "\277", "vw==" },
+ { 1, "\177", "fw==" },
+ { 2, "\000\000", "AAA=" },
+ { 2, "\000\001", "AAE=" },
+ { 2, "\000\002", "AAI=" },
+ { 2, "\000\004", "AAQ=" },
+ { 2, "\000\010", "AAg=" },
+ { 2, "\000\020", "ABA=" },
+ { 2, "\000\040", "ACA=" },
+ { 2, "\000\100", "AEA=" },
+ { 2, "\000\200", "AIA=" },
+ { 2, "\001\000", "AQA=" },
+ { 2, "\002\000", "AgA=" },
+ { 2, "\004\000", "BAA=" },
+ { 2, "\010\000", "CAA=" },
+ { 2, "\020\000", "EAA=" },
+ { 2, "\040\000", "IAA=" },
+ { 2, "\100\000", "QAA=" },
+ { 2, "\200\000", "gAA=" },
+
+ { 2, "\377\377", "//8=" },
+ { 2, "\377\376", "//4=" },
+ { 2, "\377\375", "//0=" },
+ { 2, "\377\373", "//s=" },
+ { 2, "\377\367", "//c=" },
+ { 2, "\377\357", "/+8=" },
+ { 2, "\377\337", "/98=" },
+ { 2, "\377\277", "/78=" },
+ { 2, "\377\177", "/38=" },
+ { 2, "\376\377", "/v8=" },
+ { 2, "\375\377", "/f8=" },
+ { 2, "\373\377", "+/8=" },
+ { 2, "\367\377", "9/8=" },
+ { 2, "\357\377", "7/8=" },
+ { 2, "\337\377", "3/8=" },
+ { 2, "\277\377", "v/8=" },
+ { 2, "\177\377", "f/8=" },
+
+ { 3, "\000\000\000", "AAAA" },
+ { 3, "\000\000\001", "AAAB" },
+ { 3, "\000\000\002", "AAAC" },
+ { 3, "\000\000\004", "AAAE" },
+ { 3, "\000\000\010", "AAAI" },
+ { 3, "\000\000\020", "AAAQ" },
+ { 3, "\000\000\040", "AAAg" },
+ { 3, "\000\000\100", "AABA" },
+ { 3, "\000\000\200", "AACA" },
+ { 3, "\000\001\000", "AAEA" },
+ { 3, "\000\002\000", "AAIA" },
+ { 3, "\000\004\000", "AAQA" },
+ { 3, "\000\010\000", "AAgA" },
+ { 3, "\000\020\000", "ABAA" },
+ { 3, "\000\040\000", "ACAA" },
+ { 3, "\000\100\000", "AEAA" },
+ { 3, "\000\200\000", "AIAA" },
+ { 3, "\001\000\000", "AQAA" },
+ { 3, "\002\000\000", "AgAA" },
+ { 3, "\004\000\000", "BAAA" },
+ { 3, "\010\000\000", "CAAA" },
+ { 3, "\020\000\000", "EAAA" },
+ { 3, "\040\000\000", "IAAA" },
+ { 3, "\100\000\000", "QAAA" },
+ { 3, "\200\000\000", "gAAA" },
+
+ { 3, "\377\377\377", "////" },
+ { 3, "\377\377\376", "///+" },
+ { 3, "\377\377\375", "///9" },
+ { 3, "\377\377\373", "///7" },
+ { 3, "\377\377\367", "///3" },
+ { 3, "\377\377\357", "///v" },
+ { 3, "\377\377\337", "///f" },
+ { 3, "\377\377\277", "//+/" },
+ { 3, "\377\377\177", "//9/" },
+ { 3, "\377\376\377", "//7/" },
+ { 3, "\377\375\377", "//3/" },
+ { 3, "\377\373\377", "//v/" },
+ { 3, "\377\367\377", "//f/" },
+ { 3, "\377\357\377", "/+//" },
+ { 3, "\377\337\377", "/9//" },
+ { 3, "\377\277\377", "/7//" },
+ { 3, "\377\177\377", "/3//" },
+ { 3, "\376\377\377", "/v//" },
+ { 3, "\375\377\377", "/f//" },
+ { 3, "\373\377\377", "+///" },
+ { 3, "\367\377\377", "9///" },
+ { 3, "\357\377\377", "7///" },
+ { 3, "\337\377\377", "3///" },
+ { 3, "\277\377\377", "v///" },
+ { 3, "\177\377\377", "f///" },
+
+ // Random numbers: values obtained with
+ //
+ // #! /bin/bash
+ // dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
+ // od -N $1 -t o1 /tmp/bar.random
+ // uuencode -m test < /tmp/bar.random
+ //
+ // where $1 is the number of bytes (2, 3)
+
+ { 2, "\243\361", "o/E=" },
+ { 2, "\024\167", "FHc=" },
+ { 2, "\313\252", "y6o=" },
+ { 2, "\046\041", "JiE=" },
+ { 2, "\145\236", "ZZ4=" },
+ { 2, "\254\325", "rNU=" },
+ { 2, "\061\330", "Mdg=" },
+ { 2, "\245\032", "pRo=" },
+ { 2, "\006\000", "BgA=" },
+ { 2, "\375\131", "/Vk=" },
+ { 2, "\303\210", "w4g=" },
+ { 2, "\040\037", "IB8=" },
+ { 2, "\261\372", "sfo=" },
+ { 2, "\335\014", "3Qw=" },
+ { 2, "\233\217", "m48=" },
+ { 2, "\373\056", "+y4=" },
+ { 2, "\247\232", "p5o=" },
+ { 2, "\107\053", "Rys=" },
+ { 2, "\204\077", "hD8=" },
+ { 2, "\276\211", "vok=" },
+ { 2, "\313\110", "y0g=" },
+ { 2, "\363\376", "8/4=" },
+ { 2, "\251\234", "qZw=" },
+ { 2, "\103\262", "Q7I=" },
+ { 2, "\142\312", "Yso=" },
+ { 2, "\067\211", "N4k=" },
+ { 2, "\220\001", "kAE=" },
+ { 2, "\152\240", "aqA=" },
+ { 2, "\367\061", "9zE=" },
+ { 2, "\133\255", "W60=" },
+ { 2, "\176\035", "fh0=" },
+ { 2, "\032\231", "Gpk=" },
+
+ { 3, "\013\007\144", "Cwdk" },
+ { 3, "\030\112\106", "GEpG" },
+ { 3, "\047\325\046", "J9Um" },
+ { 3, "\310\160\022", "yHAS" },
+ { 3, "\131\100\237", "WUCf" },
+ { 3, "\064\342\134", "NOJc" },
+ { 3, "\010\177\004", "CH8E" },
+ { 3, "\345\147\205", "5WeF" },
+ { 3, "\300\343\360", "wOPw" },
+ { 3, "\061\240\201", "MaCB" },
+ { 3, "\225\333\044", "ldsk" },
+ { 3, "\215\137\352", "jV/q" },
+ { 3, "\371\147\160", "+Wdw" },
+ { 3, "\030\320\051", "GNAp" },
+ { 3, "\044\174\241", "JHyh" },
+ { 3, "\260\127\037", "sFcf" },
+ { 3, "\111\045\033", "SSUb" },
+ { 3, "\202\114\107", "gkxH" },
+ { 3, "\057\371\042", "L/ki" },
+ { 3, "\223\247\244", "k6ek" },
+ { 3, "\047\216\144", "J45k" },
+ { 3, "\203\070\327", "gzjX" },
+ { 3, "\247\140\072", "p2A6" },
+ { 3, "\124\115\116", "VE1O" },
+ { 3, "\157\162\050", "b3Io" },
+ { 3, "\357\223\004", "75ME" },
+ { 3, "\052\117\156", "Kk9u" },
+ { 3, "\347\154\000", "52wA" },
+ { 3, "\303\012\142", "wwpi" },
+ { 3, "\060\035\362", "MB3y" },
+ { 3, "\130\226\361", "WJbx" },
+ { 3, "\173\013\071", "ews5" },
+ { 3, "\336\004\027", "3gQX" },
+ { 3, "\357\366\234", "7/ac" },
+ { 3, "\353\304\111", "68RJ" },
+ { 3, "\024\264\131", "FLRZ" },
+ { 3, "\075\114\251", "PUyp" },
+ { 3, "\315\031\225", "zRmV" },
+ { 3, "\154\201\276", "bIG+" },
+ { 3, "\200\066\072", "gDY6" },
+ { 3, "\142\350\267", "Yui3" },
+ { 3, "\033\000\166", "GwB2" },
+ { 3, "\210\055\077", "iC0/" },
+ { 3, "\341\037\124", "4R9U" },
+ { 3, "\161\103\152", "cUNq" },
+ { 3, "\270\142\131", "uGJZ" },
+ { 3, "\337\076\074", "3z48" },
+ { 3, "\375\106\362", "/Uby" },
+ { 3, "\227\301\127", "l8FX" },
+ { 3, "\340\002\234", "4AKc" },
+ { 3, "\121\064\033", "UTQb" },
+ { 3, "\157\134\143", "b1xj" },
+ { 3, "\247\055\327", "py3X" },
+ { 3, "\340\142\005", "4GIF" },
+ { 3, "\060\260\143", "MLBj" },
+ { 3, "\075\203\170", "PYN4" },
+ { 3, "\143\160\016", "Y3AO" },
+ { 3, "\313\013\063", "ywsz" },
+ { 3, "\174\236\135", "fJ5d" },
+ { 3, "\103\047\026", "QycW" },
+ { 3, "\365\005\343", "9QXj" },
+ { 3, "\271\160\223", "uXCT" },
+ { 3, "\362\255\172", "8q16" },
+ { 3, "\113\012\015", "SwoN" },
+
+ // various lengths, generated by this python script:
+ //
+ // from string import lowercase as lc
+ // for i in range(27):
+ // print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
+ // lc[:i].encode('base64').strip())
+
+ { 0, "", "" },
+ { 1, "a", "YQ==" },
+ { 2, "ab", "YWI=" },
+ { 3, "abc", "YWJj" },
+ { 4, "abcd", "YWJjZA==" },
+ { 5, "abcde", "YWJjZGU=" },
+ { 6, "abcdef", "YWJjZGVm" },
+ { 7, "abcdefg", "YWJjZGVmZw==" },
+ { 8, "abcdefgh", "YWJjZGVmZ2g=" },
+ { 9, "abcdefghi", "YWJjZGVmZ2hp" },
+ { 10, "abcdefghij", "YWJjZGVmZ2hpag==" },
+ { 11, "abcdefghijk", "YWJjZGVmZ2hpams=" },
+ { 12, "abcdefghijkl", "YWJjZGVmZ2hpamts" },
+ { 13, "abcdefghijklm", "YWJjZGVmZ2hpamtsbQ==" },
+ { 14, "abcdefghijklmn", "YWJjZGVmZ2hpamtsbW4=" },
+ { 15, "abcdefghijklmno", "YWJjZGVmZ2hpamtsbW5v" },
+ { 16, "abcdefghijklmnop", "YWJjZGVmZ2hpamtsbW5vcA==" },
+ { 17, "abcdefghijklmnopq", "YWJjZGVmZ2hpamtsbW5vcHE=" },
+ { 18, "abcdefghijklmnopqr", "YWJjZGVmZ2hpamtsbW5vcHFy" },
+ { 19, "abcdefghijklmnopqrs", "YWJjZGVmZ2hpamtsbW5vcHFycw==" },
+ { 20, "abcdefghijklmnopqrst", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q=" },
+ { 21, "abcdefghijklmnopqrstu", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1" },
+ { 22, "abcdefghijklmnopqrstuv", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg==" },
+ { 23, "abcdefghijklmnopqrstuvw", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc=" },
+ { 24, "abcdefghijklmnopqrstuvwx", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4" },
+ { 25, "abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ==" },
+ { 26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=" },
+};
+
+static struct {
+ const char* plaintext;
+ const char* cyphertext;
+} base64_strings[] = {
+ // Some google quotes
+ // Cyphertext created with "uuencode (GNU sharutils) 4.6.3"
+ // (Note that we're testing the websafe encoding, though, so if
+ // you add messages, be sure to run "tr -- '+/' '-_'" on the output)
+ { "I was always good at math and science, and I never realized "
+ "that was unusual or somehow undesirable. So one of the things "
+ "I care a lot about is helping to remove that stigma, "
+ "to show girls that you can be feminine, you can like the things "
+ "that girls like, but you can also be really good at technology. "
+ "You can be really good at building things."
+ " - Marissa Meyer, Newsweek, 2010-12-22" "\n",
+
+ "SSB3YXMgYWx3YXlzIGdvb2QgYXQgbWF0aCBhbmQgc2NpZW5jZSwgYW5kIEkg"
+ "bmV2ZXIgcmVhbGl6ZWQgdGhhdCB3YXMgdW51c3VhbCBvciBzb21laG93IHVu"
+ "ZGVzaXJhYmxlLiBTbyBvbmUgb2YgdGhlIHRoaW5ncyBJIGNhcmUgYSBsb3Qg"
+ "YWJvdXQgaXMgaGVscGluZyB0byByZW1vdmUgdGhhdCBzdGlnbWEsIHRvIHNo"
+ "b3cgZ2lybHMgdGhhdCB5b3UgY2FuIGJlIGZlbWluaW5lLCB5b3UgY2FuIGxp"
+ "a2UgdGhlIHRoaW5ncyB0aGF0IGdpcmxzIGxpa2UsIGJ1dCB5b3UgY2FuIGFs"
+ "c28gYmUgcmVhbGx5IGdvb2QgYXQgdGVjaG5vbG9neS4gWW91IGNhbiBiZSBy"
+ "ZWFsbHkgZ29vZCBhdCBidWlsZGluZyB0aGluZ3MuIC0gTWFyaXNzYSBNZXll"
+ "ciwgTmV3c3dlZWssIDIwMTAtMTItMjIK" },
+
+ { "Typical first year for a new cluster: "
+ "~0.5 overheating "
+ "~1 PDU failure "
+ "~1 rack-move "
+ "~1 network rewiring "
+ "~20 rack failures "
+ "~5 racks go wonky "
+ "~8 network maintenances "
+ "~12 router reloads "
+ "~3 router failures "
+ "~dozens of minor 30-second blips for dns "
+ "~1000 individual machine failures "
+ "~thousands of hard drive failures "
+ "slow disks, bad memory, misconfigured machines, flaky machines, etc."
+ " - Jeff Dean, The Joys of Real Hardware" "\n",
+
+ "VHlwaWNhbCBmaXJzdCB5ZWFyIGZvciBhIG5ldyBjbHVzdGVyOiB-MC41IG92"
+ "ZXJoZWF0aW5nIH4xIFBEVSBmYWlsdXJlIH4xIHJhY2stbW92ZSB-MSBuZXR3"
+ "b3JrIHJld2lyaW5nIH4yMCByYWNrIGZhaWx1cmVzIH41IHJhY2tzIGdvIHdv"
+ "bmt5IH44IG5ldHdvcmsgbWFpbnRlbmFuY2VzIH4xMiByb3V0ZXIgcmVsb2Fk"
+ "cyB-MyByb3V0ZXIgZmFpbHVyZXMgfmRvemVucyBvZiBtaW5vciAzMC1zZWNv"
+ "bmQgYmxpcHMgZm9yIGRucyB-MTAwMCBpbmRpdmlkdWFsIG1hY2hpbmUgZmFp"
+ "bHVyZXMgfnRob3VzYW5kcyBvZiBoYXJkIGRyaXZlIGZhaWx1cmVzIHNsb3cg"
+ "ZGlza3MsIGJhZCBtZW1vcnksIG1pc2NvbmZpZ3VyZWQgbWFjaGluZXMsIGZs"
+ "YWt5IG1hY2hpbmVzLCBldGMuIC0gSmVmZiBEZWFuLCBUaGUgSm95cyBvZiBS"
+ "ZWFsIEhhcmR3YXJlCg" },
+
+ { "I'm the head of the webspam team at Google. "
+ "That means that if you type your name into Google and get porn back, "
+ "it's my fault. Unless you're a porn star, in which case porn is a "
+ "completely reasonable response."
+ " - Matt Cutts, Google Plus" "\n",
+
+ "SSdtIHRoZSBoZWFkIG9mIHRoZSB3ZWJzcGFtIHRlYW0gYXQgR29vZ2xlLiAg"
+ "VGhhdCBtZWFucyB0aGF0IGlmIHlvdSB0eXBlIHlvdXIgbmFtZSBpbnRvIEdv"
+ "b2dsZSBhbmQgZ2V0IHBvcm4gYmFjaywgaXQncyBteSBmYXVsdC4gVW5sZXNz"
+ "IHlvdSdyZSBhIHBvcm4gc3RhciwgaW4gd2hpY2ggY2FzZSBwb3JuIGlzIGEg"
+ "Y29tcGxldGVseSByZWFzb25hYmxlIHJlc3BvbnNlLiAtIE1hdHQgQ3V0dHMs"
+ "IEdvb2dsZSBQbHVzCg" },
+
+ { "It will still be a long time before machines approach human intelligence. "
+ "But luckily, machines don't actually have to be intelligent; "
+ "they just have to fake it. Access to a wealth of information, "
+ "combined with a rudimentary decision-making capacity, "
+ "can often be almost as useful. Of course, the results are better yet "
+ "when coupled with intelligence. A reference librarian with access to "
+ "a good search engine is a formidable tool."
+ " - Craig Silverstein, Siemens Pictures of the Future, Spring 2004" "\n",
+
+ "SXQgd2lsbCBzdGlsbCBiZSBhIGxvbmcgdGltZSBiZWZvcmUgbWFjaGluZXMg"
+ "YXBwcm9hY2ggaHVtYW4gaW50ZWxsaWdlbmNlLiBCdXQgbHVja2lseSwgbWFj"
+ "aGluZXMgZG9uJ3QgYWN0dWFsbHkgaGF2ZSB0byBiZSBpbnRlbGxpZ2VudDsg"
+ "dGhleSBqdXN0IGhhdmUgdG8gZmFrZSBpdC4gQWNjZXNzIHRvIGEgd2VhbHRo"
+ "IG9mIGluZm9ybWF0aW9uLCBjb21iaW5lZCB3aXRoIGEgcnVkaW1lbnRhcnkg"
+ "ZGVjaXNpb24tbWFraW5nIGNhcGFjaXR5LCBjYW4gb2Z0ZW4gYmUgYWxtb3N0"
+ "IGFzIHVzZWZ1bC4gT2YgY291cnNlLCB0aGUgcmVzdWx0cyBhcmUgYmV0dGVy"
+ "IHlldCB3aGVuIGNvdXBsZWQgd2l0aCBpbnRlbGxpZ2VuY2UuIEEgcmVmZXJl"
+ "bmNlIGxpYnJhcmlhbiB3aXRoIGFjY2VzcyB0byBhIGdvb2Qgc2VhcmNoIGVu"
+ "Z2luZSBpcyBhIGZvcm1pZGFibGUgdG9vbC4gLSBDcmFpZyBTaWx2ZXJzdGVp"
+ "biwgU2llbWVucyBQaWN0dXJlcyBvZiB0aGUgRnV0dXJlLCBTcHJpbmcgMjAw"
+ "NAo" },
+
+ // Degenerate edge case
+ { "",
+ "" },
+};
+
+TEST(Base64, EscapeAndUnescape) {
+ // Check the short strings; this tests the math (and boundaries)
+ for (int i = 0; i < sizeof(base64_tests) / sizeof(base64_tests[0]); ++i) {
+ char encode_buffer[100];
+ int encode_length;
+ char decode_buffer[100];
+ int decode_length;
+ int cypher_length;
+ string decode_str;
+
+ const unsigned char* unsigned_plaintext =
+ reinterpret_cast<const unsigned char*>(base64_tests[i].plaintext);
+
+ StringPiece plaintext(base64_tests[i].plaintext,
+ base64_tests[i].plain_length);
+
+ cypher_length = strlen(base64_tests[i].cyphertext);
+
+ // The basic escape function:
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = Base64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ sizeof(encode_buffer));
+ // Is it of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+ // Would it have been okay to allocate only CalculateBase64EscapeLen()?
+ EXPECT_EQ(CalculateBase64EscapedLen(base64_tests[i].plain_length),
+ encode_length);
+
+ // Is it the expected encoded value?
+ ASSERT_STREQ(encode_buffer, base64_tests[i].cyphertext);
+
+ // If we encode it into a buffer of exactly the right length...
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = Base64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ cypher_length);
+ // Is it still of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+
+ // And is the value still correct? (i.e., not losing the last byte)
+ EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext);
+
+ // If we decode it back:
+ decode_str.clear();
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decode_str));
+
+ // Is it of the expected length?
+ EXPECT_EQ(base64_tests[i].plain_length, decode_str.length());
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(plaintext, decode_str);
+
+ // Let's try with a pre-populated string.
+ string encoded("this junk should be ignored");
+ Base64Escape(string(base64_tests[i].plaintext,
+ base64_tests[i].plain_length),
+ &encoded);
+ EXPECT_EQ(encoded, string(encode_buffer, cypher_length));
+
+ string decoded("this junk should be ignored");
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decoded));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Our decoder treats the padding '=' characters at the end as
+ // optional (but if there are any, there must be the correct
+ // number of them.) If encode_buffer has any, run some additional
+ // tests that fiddle with them.
+ char* first_equals = strchr(encode_buffer, '=');
+ if (first_equals) {
+ // How many equals signs does the string start with?
+ int equals = (*(first_equals+1) == '=') ? 2 : 1;
+
+ // Try chopping off the equals sign(s) entirely. The decoder
+ // should still be okay with this.
+ string decoded2("this junk should also be ignored");
+ *first_equals = '\0';
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, first_equals - encode_buffer), &decoded2));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Now test chopping off the equals sign(s) and adding
+ // whitespace. Our decoder should still accept this.
+ decoded2.assign("this junk should be ignored");
+ *first_equals = ' ';
+ *(first_equals+1) = '\0';
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, first_equals - encode_buffer + 1),
+ &decoded2));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Now stick a bad character at the end of the string. The decoder
+ // should refuse this string.
+ decoded2.assign("this junk should be ignored");
+ *first_equals = '?';
+ *(first_equals+1) = '\0';
+ EXPECT_TRUE(
+ !Base64Unescape(
+ StringPiece(encode_buffer, first_equals - encode_buffer + 1),
+ &decoded2));
+
+ int len;
+
+ // Test whitespace mixed with the padding. (eg "AA = = ") The
+ // decoder should accept this.
+ if (equals == 2) {
+ snprintf(first_equals, 6, " = = ");
+ len = first_equals - encode_buffer + 5;
+ } else {
+ snprintf(first_equals, 6, " = ");
+ len = first_equals - encode_buffer + 3;
+ }
+ decoded2.assign("this junk should be ignored");
+ EXPECT_TRUE(
+ Base64Unescape(StringPiece(encode_buffer, len), &decoded2));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Test whitespace mixed with the padding, but with the wrong
+ // number of equals signs (eg "AA = "). The decoder should
+ // refuse these strings.
+ if (equals == 1) {
+ snprintf(first_equals, 6, " = = ");
+ len = first_equals - encode_buffer + 5;
+ } else {
+ snprintf(first_equals, 6, " = ");
+ len = first_equals - encode_buffer + 3;
+ }
+ EXPECT_TRUE(
+ !Base64Unescape(StringPiece(encode_buffer, len), &decoded2));
+ }
+
+ // Cool! the basic Base64 encoder/decoder works.
+ // Let's try the alternate alphabet: tr -- '+/' '-_'
+
+ char websafe[100];
+ memset(websafe, 0, sizeof(websafe));
+ strncpy(websafe, base64_tests[i].cyphertext, cypher_length);
+ for (int c = 0; c < sizeof(websafe); ++c) {
+ if ('+' == websafe[c]) { websafe[c] = '-'; }
+ if ('/' == websafe[c]) { websafe[c] = '_'; }
+ }
+
+ // The websafe escape function:
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ sizeof(encode_buffer),
+ true);
+ // Is it of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+ EXPECT_EQ(
+ CalculateBase64EscapedLen(base64_tests[i].plain_length, true),
+ encode_length);
+
+ // Is it the expected encoded value?
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // If we encode it into a buffer of exactly the right length...
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ cypher_length,
+ true);
+ // Is it still of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+
+ // And is the value still correct? (i.e., not losing the last byte)
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // Let's try the string version of the encoder
+ encoded = "this junk should be ignored";
+ WebSafeBase64Escape(
+ unsigned_plaintext, base64_tests[i].plain_length,
+ &encoded, true);
+ EXPECT_EQ(encoded.size(), cypher_length);
+ EXPECT_STREQ(encoded.c_str(), websafe);
+
+ // If we decode it back:
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ sizeof(decode_buffer));
+
+ // Is it of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // If we decode it into a buffer of exactly the right length...
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ decode_length);
+
+ // Is it still of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // And is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // Try using '.' for the pad character.
+ for (int c = cypher_length - 1; c >= 0 && '=' == encode_buffer[c]; --c) {
+ encode_buffer[c] = '.';
+ }
+
+ // If we decode it back:
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ sizeof(decode_buffer));
+
+ // Is it of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // If we decode it into a buffer of exactly the right length...
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ decode_length);
+
+ // Is it still of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // And is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // Let's try the string version of the decoder
+ decoded = "this junk should be ignored";
+ EXPECT_TRUE(WebSafeBase64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decoded));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Okay! the websafe Base64 encoder/decoder works.
+ // Let's try the unpadded version
+
+ for (int c = 0; c < sizeof(websafe); ++c) {
+ if ('=' == websafe[c]) {
+ websafe[c] = '\0';
+ cypher_length = c;
+ break;
+ }
+ }
+
+ // The websafe escape function:
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ sizeof(encode_buffer),
+ false);
+ // Is it of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+ EXPECT_EQ(
+ CalculateBase64EscapedLen(base64_tests[i].plain_length, false),
+ encode_length);
+
+ // Is it the expected encoded value?
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // If we encode it into a buffer of exactly the right length...
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ cypher_length,
+ false);
+ // Is it still of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+
+ // And is the value still correct? (i.e., not losing the last byte)
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // Let's try the (other) string version of the encoder
+ string plain(base64_tests[i].plaintext, base64_tests[i].plain_length);
+ encoded = "this junk should be ignored";
+ WebSafeBase64Escape(plain, &encoded);
+ EXPECT_EQ(encoded.size(), cypher_length);
+ EXPECT_STREQ(encoded.c_str(), websafe);
+
+ // If we decode it back:
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ sizeof(decode_buffer));
+
+ // Is it of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // If we decode it into a buffer of exactly the right length...
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ decode_length);
+
+ // Is it still of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // And is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+
+ // Let's try the string version of the decoder
+ decoded = "this junk should be ignored";
+ EXPECT_TRUE(WebSafeBase64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decoded));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // This value works. Try the next.
+ }
+
+ // Now try the long strings, this tests the streaming
+ for (int i = 0; i < sizeof(base64_strings) / sizeof(base64_strings[0]);
+ ++i) {
+ const unsigned char* unsigned_plaintext =
+ reinterpret_cast<const unsigned char*>(base64_strings[i].plaintext);
+ int plain_length = strlen(base64_strings[i].plaintext);
+ int cypher_length = strlen(base64_strings[i].cyphertext);
+ vector<char> buffer(cypher_length+1);
+ int encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ plain_length,
+ &buffer[0],
+ buffer.size(),
+ false);
+ EXPECT_EQ(cypher_length, encode_length);
+ EXPECT_EQ(
+ CalculateBase64EscapedLen(plain_length, false), encode_length);
+ buffer[ encode_length ] = '\0';
+ EXPECT_STREQ(base64_strings[i].cyphertext, &buffer[0]);
+ }
+
+ // Verify the behavior when decoding bad data
+ {
+ const char* bad_data = "ab-/";
+ string buf;
+ EXPECT_FALSE(Base64Unescape(StringPiece(bad_data), &buf));
+ EXPECT_TRUE(!WebSafeBase64Unescape(bad_data, &buf));
+ EXPECT_TRUE(buf.empty());
+ }
+}
+
} // anonymous namespace
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/time.cc b/src/google/protobuf/stubs/time.cc
new file mode 100644
index 00000000..3319a244
--- /dev/null
+++ b/src/google/protobuf/stubs/time.cc
@@ -0,0 +1,366 @@
+#include <google/protobuf/stubs/time.h>
+
+#include <ctime>
+
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+namespace {
+static const int64 kSecondsPerMinute = 60;
+static const int64 kSecondsPerHour = 3600;
+static const int64 kSecondsPerDay = kSecondsPerHour * 24;
+static const int64 kSecondsPer400Years =
+ kSecondsPerDay * (400 * 365 + 400 / 4 - 3);
+// Seconds from 0001-01-01T00:00:00 to 1970-01-01T:00:00:00
+static const int64 kSecondsFromEraToEpoch = 62135596800LL;
+// The range of timestamp values we support.
+static const int64 kMinTime = -62135596800LL; // 0001-01-01T00:00:00
+static const int64 kMaxTime = 253402300799LL; // 9999-12-31T23:59:59
+
+static const int kNanosPerSecond = 1000000000;
+static const int kNanosPerMillisecond = 1000000;
+static const int kNanosPerMicrosecond = 1000;
+
+// Count the seconds from the given year (start at Jan 1, 00:00) to 100 years
+// after.
+int64 SecondsPer100Years(int year) {
+ if (year % 400 == 0 || year % 400 > 300) {
+ return kSecondsPerDay * (100 * 365 + 100 / 4);
+ } else {
+ return kSecondsPerDay * (100 * 365 + 100 / 4 - 1);
+ }
+}
+
+// Count the seconds from the given year (start at Jan 1, 00:00) to 4 years
+// after.
+int64 SecondsPer4Years(int year) {
+ if ((year % 100 == 0 || year % 100 > 96) &&
+ !(year % 400 == 0 || year % 400 > 396)) {
+ // No leap years.
+ return kSecondsPerDay * (4 * 365);
+ } else {
+ // One leap years.
+ return kSecondsPerDay * (4 * 365 + 1);
+ }
+}
+
+bool IsLeapYear(int year) {
+ return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
+}
+
+int64 SecondsPerYear(int year) {
+ return kSecondsPerDay * (IsLeapYear(year) ? 366 : 365);
+}
+
+static const int kDaysInMonth[13] = {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+int64 SecondsPerMonth(int month, bool leap) {
+ if (month == 2 && leap) {
+ return kSecondsPerDay * (kDaysInMonth[month] + 1);
+ }
+ return kSecondsPerDay * kDaysInMonth[month];
+}
+
+static const int kDaysSinceJan[13] = {
+ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+};
+
+bool ValidateDateTime(const DateTime& time) {
+ if (time.year < 1 || time.year > 9999 ||
+ time.month < 1 || time.month > 12 ||
+ time.day < 1 || time.day > 31 ||
+ time.hour < 0 || time.hour > 23 ||
+ time.minute < 0 || time.minute > 59 ||
+ time.second < 0 || time.second > 59) {
+ return false;
+ }
+ if (time.month == 2 && IsLeapYear(time.year)) {
+ return time.month <= kDaysInMonth[time.month] + 1;
+ } else {
+ return time.month <= kDaysInMonth[time.month];
+ }
+}
+
+// Count the number of seconds elapsed from 0001-01-01T00:00:00 to the given
+// time.
+int64 SecondsSinceCommonEra(const DateTime& time) {
+ int64 result = 0;
+ // Years should be between 1 and 9999.
+ assert(time.year >= 1 && time.year <= 9999);
+ int year = 1;
+ if ((time.year - year) >= 400) {
+ int count_400years = (time.year - year) / 400;
+ result += kSecondsPer400Years * count_400years;
+ year += count_400years * 400;
+ }
+ while ((time.year - year) >= 100) {
+ result += SecondsPer100Years(year);
+ year += 100;
+ }
+ while ((time.year - year) >= 4) {
+ result += SecondsPer4Years(year);
+ year += 4;
+ }
+ while (time.year > year) {
+ result += SecondsPerYear(year);
+ ++year;
+ }
+ // Months should be between 1 and 12.
+ assert(time.month >= 1 && time.month <= 12);
+ int month = time.month;
+ result += kSecondsPerDay * kDaysSinceJan[month];
+ if (month > 2 && IsLeapYear(year)) {
+ result += kSecondsPerDay;
+ }
+ assert(time.day >= 1 &&
+ time.day <= (month == 2 && IsLeapYear(year)
+ ? kDaysInMonth[month] + 1
+ : kDaysInMonth[month]));
+ result += kSecondsPerDay * (time.day - 1);
+ result += kSecondsPerHour * time.hour +
+ kSecondsPerMinute * time.minute +
+ time.second;
+ return result;
+}
+
+// Format nanoseconds with either 3, 6, or 9 digits depending on the required
+// precision to represent the exact value.
+string FormatNanos(int32 nanos) {
+ if (nanos % kNanosPerMillisecond == 0) {
+ return StringPrintf("%03d", nanos / kNanosPerMillisecond);
+ } else if (nanos % kNanosPerMicrosecond == 0) {
+ return StringPrintf("%06d", nanos / kNanosPerMicrosecond);
+ } else {
+ return StringPrintf("%09d", nanos);
+ }
+}
+
+// Parses an integer from a null-terminated char sequence. The method
+// consumes at most "width" chars. Returns a pointer after the consumed
+// integer, or NULL if the data does not start with an integer or the
+// integer value does not fall in the range of [min_value, max_value].
+const char* ParseInt(const char* data, int width, int min_value,
+ int max_value, int* result) {
+ if (!ascii_isdigit(*data)) {
+ return NULL;
+ }
+ int value = 0;
+ for (int i = 0; i < width; ++i, ++data) {
+ if (ascii_isdigit(*data)) {
+ value = value * 10 + (*data - '0');
+ } else {
+ break;
+ }
+ }
+ if (value >= min_value && value <= max_value) {
+ *result = value;
+ return data;
+ } else {
+ return NULL;
+ }
+}
+
+// Consumes the fractional parts of a second into nanos. For example,
+// "010" will be parsed to 10000000 nanos.
+const char* ParseNanos(const char* data, int32* nanos) {
+ if (!ascii_isdigit(*data)) {
+ return NULL;
+ }
+ int value = 0;
+ int len = 0;
+ // Consume as many digits as there are but only take the first 9 into
+ // account.
+ while (ascii_isdigit(*data)) {
+ if (len < 9) {
+ value = value * 10 + *data - '0';
+ }
+ ++len;
+ ++data;
+ }
+ while (len < 9) {
+ value = value * 10;
+ ++len;
+ }
+ *nanos = value;
+ return data;
+}
+
+const char* ParseTimezoneOffset(const char* data, int64* offset) {
+ // Accept format "HH:MM". E.g., "08:00"
+ int hour;
+ if ((data = ParseInt(data, 2, 0, 23, &hour)) == NULL) {
+ return NULL;
+ }
+ if (*data++ != ':') {
+ return NULL;
+ }
+ int minute;
+ if ((data = ParseInt(data, 2, 0, 59, &minute)) == NULL) {
+ return NULL;
+ }
+ *offset = (hour * 60 + minute) * 60;
+ return data;
+}
+} // namespace
+
+bool SecondsToDateTime(int64 seconds, DateTime* time) {
+ if (seconds < kMinTime || seconds > kMaxTime) {
+ return false;
+ }
+ // It's easier to calcuate the DateTime starting from 0001-01-01T00:00:00
+ seconds = seconds + kSecondsFromEraToEpoch;
+ int year = 1;
+ if (seconds >= kSecondsPer400Years) {
+ int count_400years = seconds / kSecondsPer400Years;
+ year += 400 * count_400years;
+ seconds %= kSecondsPer400Years;
+ }
+ while (seconds >= SecondsPer100Years(year)) {
+ seconds -= SecondsPer100Years(year);
+ year += 100;
+ }
+ while (seconds >= SecondsPer4Years(year)) {
+ seconds -= SecondsPer4Years(year);
+ year += 4;
+ }
+ while (seconds >= SecondsPerYear(year)) {
+ seconds -= SecondsPerYear(year);
+ year += 1;
+ }
+ bool leap = IsLeapYear(year);
+ int month = 1;
+ while (seconds >= SecondsPerMonth(month, leap)) {
+ seconds -= SecondsPerMonth(month, leap);
+ ++month;
+ }
+ int day = 1 + seconds / kSecondsPerDay;
+ seconds %= kSecondsPerDay;
+ int hour = seconds / kSecondsPerHour;
+ seconds %= kSecondsPerHour;
+ int minute = seconds / kSecondsPerMinute;
+ seconds %= kSecondsPerMinute;
+ time->year = year;
+ time->month = month;
+ time->day = day;
+ time->hour = hour;
+ time->minute = minute;
+ time->second = static_cast<int>(seconds);
+ return true;
+}
+
+bool DateTimeToSeconds(const DateTime& time, int64* seconds) {
+ if (!ValidateDateTime(time)) {
+ return false;
+ }
+ *seconds = SecondsSinceCommonEra(time) - kSecondsFromEraToEpoch;
+ return true;
+}
+
+void GetCurrentTime(int64* seconds, int32* nanos) {
+ // TODO(xiaofeng): Improve the accuracy of this implementation (or just
+ // remove this method from protobuf).
+ *seconds = time(NULL);
+ *nanos = 0;
+}
+
+string FormatTime(int64 seconds, int32 nanos) {
+ DateTime time;
+ if (nanos < 0 || nanos > 999999999 || !SecondsToDateTime(seconds, &time)) {
+ return "InvalidTime";
+ }
+ string result = StringPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
+ time.year, time.month, time.day,
+ time.hour, time.minute, time.second);
+ if (nanos != 0) {
+ result += "." + FormatNanos(nanos);
+ }
+ return result + "Z";
+}
+
+bool ParseTime(const string& value, int64* seconds, int32* nanos) {
+ DateTime time;
+ const char* data = value.c_str();
+ // We only accept:
+ // Z-normalized: 2015-05-20T13:29:35.120Z
+ // With UTC offset: 2015-05-20T13:29:35.120-08:00
+
+ // Parse year
+ if ((data = ParseInt(data, 4, 1, 9999, &time.year)) == NULL) {
+ return false;
+ }
+ // Expect '-'
+ if (*data++ != '-') return false;
+ // Parse month
+ if ((data = ParseInt(data, 2, 1, 12, &time.month)) == NULL) {
+ return false;
+ }
+ // Expect '-'
+ if (*data++ != '-') return false;
+ // Parse day
+ if ((data = ParseInt(data, 2, 1, 31, &time.day)) == NULL) {
+ return false;
+ }
+ // Expect 'T'
+ if (*data++ != 'T') return false;
+ // Parse hour
+ if ((data = ParseInt(data, 2, 0, 23, &time.hour)) == NULL) {
+ return false;
+ }
+ // Expect ':'
+ if (*data++ != ':') return false;
+ // Parse minute
+ if ((data = ParseInt(data, 2, 0, 59, &time.minute)) == NULL) {
+ return false;
+ }
+ // Expect ':'
+ if (*data++ != ':') return false;
+ // Parse second
+ if ((data = ParseInt(data, 2, 0, 59, &time.second)) == NULL) {
+ return false;
+ }
+ if (!DateTimeToSeconds(time, seconds)) {
+ return false;
+ }
+ // Parse nanoseconds.
+ if (*data == '.') {
+ ++data;
+ // Parse nanoseconds.
+ if ((data = ParseNanos(data, nanos)) == NULL) {
+ return false;
+ }
+ } else {
+ *nanos = 0;
+ }
+ // Parse UTC offsets.
+ if (*data == 'Z') {
+ ++data;
+ } else if (*data == '+') {
+ ++data;
+ int64 offset;
+ if ((data = ParseTimezoneOffset(data, &offset)) == NULL) {
+ return false;
+ }
+ *seconds -= offset;
+ } else if (*data == '-') {
+ ++data;
+ int64 offset;
+ if ((data = ParseTimezoneOffset(data, &offset)) == NULL) {
+ return false;
+ }
+ *seconds += offset;
+ } else {
+ return false;
+ }
+ // Done with parsing.
+ return *data == 0;
+}
+
+} // namespace internal
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/time.h b/src/google/protobuf/stubs/time.h
new file mode 100644
index 00000000..20a6b56d
--- /dev/null
+++ b/src/google/protobuf/stubs/time.h
@@ -0,0 +1,75 @@
+// 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 GOOGLE_PROTOBUF_STUBS_TIME_H_
+#define GOOGLE_PROTOBUF_STUBS_TIME_H_
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+struct DateTime {
+ int year;
+ int month;
+ int day;
+ int hour;
+ int minute;
+ int second;
+};
+
+// Converts a timestamp (seconds elapsed since 1970-01-01T00:00:00, could be
+// negative to represent time before 1970-01-01) to DateTime. Returns false
+// if the timestamp is not in the range between 0001-01-01T00:00:00 and
+// 9999-12-31T23:59:59.
+bool LIBPROTOBUF_EXPORT SecondsToDateTime(int64 seconds, DateTime* time);
+// Converts DateTime to a timestamp (seconds since 1970-01-01T00:00:00).
+// Returns false if the DateTime is not valid or is not in the valid range.
+bool LIBPROTOBUF_EXPORT DateTimeToSeconds(const DateTime& time, int64* seconds);
+
+void LIBPROTOBUF_EXPORT GetCurrentTime(int64* seconds, int32* nanos);
+
+// Formats a time string in RFC3339 fromat.
+//
+// For example, "2015-05-20T13:29:35.120Z". For nanos, 0, 3, 6 or 9 fractional
+// digits will be used depending on how many are required to represent the exact
+// value.
+//
+// Note that "nanos" must in the range of [0, 999999999].
+string LIBPROTOBUF_EXPORT FormatTime(int64 seconds, int32 nanos);
+// Parses a time string. This method accepts RFC3339 date/time string with UTC
+// offset. For example, "2015-05-20T13:29:35.120-08:00".
+bool LIBPROTOBUF_EXPORT ParseTime(const string& vaule, int64* seconds, int32* nanos);
+
+} // namespace internal
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_TIME_H_
diff --git a/src/google/protobuf/stubs/time_test.cc b/src/google/protobuf/stubs/time_test.cc
new file mode 100644
index 00000000..59e9d1c7
--- /dev/null
+++ b/src/google/protobuf/stubs/time_test.cc
@@ -0,0 +1,208 @@
+// 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 <google/protobuf/stubs/time.h>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+namespace {
+static const int64 kSecondsPerDay = 3600 * 24;
+
+// For DateTime, tests will mostly focuse on the date part because that's
+// the tricky one.
+int64 CreateTimestamp(int year, int month, int day) {
+ DateTime time;
+ time.year = year;
+ time.month = month;
+ time.day = day;
+ time.hour = time.minute = time.second = 0;
+ int64 result;
+ GOOGLE_CHECK(DateTimeToSeconds(time, &result));
+ // Check that a roundtrip produces the same result.
+ GOOGLE_CHECK(SecondsToDateTime(result, &time));
+ GOOGLE_CHECK(time.year == year);
+ GOOGLE_CHECK(time.month == month);
+ GOOGLE_CHECK(time.day == day);
+ return result;
+}
+
+TEST(DateTimeTest, SimpleTime) {
+ DateTime time;
+ ASSERT_TRUE(SecondsToDateTime(1, &time));
+ EXPECT_EQ(1970, time.year);
+ EXPECT_EQ(1, time.month);
+ EXPECT_EQ(1, time.day);
+ EXPECT_EQ(0, time.hour);
+ EXPECT_EQ(0, time.minute);
+ EXPECT_EQ(1, time.second);
+ int64 seconds;
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(1, seconds);
+
+ ASSERT_TRUE(SecondsToDateTime(-1, &time));
+ EXPECT_EQ(1969, time.year);
+ EXPECT_EQ(12, time.month);
+ EXPECT_EQ(31, time.day);
+ EXPECT_EQ(23, time.hour);
+ EXPECT_EQ(59, time.minute);
+ EXPECT_EQ(59, time.second);
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(-1, seconds);
+
+ DateTime start, end;
+ start.year = 1;
+ start.month = 1;
+ start.day = 1;
+ start.hour = 0;
+ start.minute = 0;
+ start.second = 0;
+ end.year = 9999;
+ end.month = 12;
+ end.day = 31;
+ end.hour = 23;
+ end.minute = 59;
+ end.second = 59;
+ int64 start_time, end_time;
+ ASSERT_TRUE(DateTimeToSeconds(start, &start_time));
+ ASSERT_TRUE(DateTimeToSeconds(end, &end_time));
+ EXPECT_EQ(315537897599LL, end_time - start_time);
+ ASSERT_TRUE(SecondsToDateTime(start_time, &time));
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(start_time, seconds);
+ ASSERT_TRUE(SecondsToDateTime(end_time, &time));
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(end_time, seconds);
+}
+
+TEST(DateTimeTest, DayInMonths) {
+ // Check that month boundaries are handled correctly.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 1, 1) - CreateTimestamp(2014, 12, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 2, 1) - CreateTimestamp(2015, 1, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 3, 1) - CreateTimestamp(2015, 2, 28));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 4, 1) - CreateTimestamp(2015, 3, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 5, 1) - CreateTimestamp(2015, 4, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 6, 1) - CreateTimestamp(2015, 5, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 7, 1) - CreateTimestamp(2015, 6, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 8, 1) - CreateTimestamp(2015, 7, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 9, 1) - CreateTimestamp(2015, 8, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 10, 1) - CreateTimestamp(2015, 9, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 11, 1) - CreateTimestamp(2015, 10, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 12, 1) - CreateTimestamp(2015, 11, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2016, 1, 1) - CreateTimestamp(2015, 12, 31));
+}
+
+TEST(DateTimeTest, LeapYear) {
+ // Non-leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 3, 1) - CreateTimestamp(2015, 2, 28));
+ // Leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2016, 3, 1) - CreateTimestamp(2016, 2, 29));
+ // Non-leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2100, 3, 1) - CreateTimestamp(2100, 2, 28));
+ // Leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2400, 3, 1) - CreateTimestamp(2400, 2, 29));
+}
+
+TEST(DateTimeTest, StringFormat) {
+ DateTime start, end;
+ start.year = 1;
+ start.month = 1;
+ start.day = 1;
+ start.hour = 0;
+ start.minute = 0;
+ start.second = 0;
+ end.year = 9999;
+ end.month = 12;
+ end.day = 31;
+ end.hour = 23;
+ end.minute = 59;
+ end.second = 59;
+ int64 start_time, end_time;
+ ASSERT_TRUE(DateTimeToSeconds(start, &start_time));
+ ASSERT_TRUE(DateTimeToSeconds(end, &end_time));
+
+ EXPECT_EQ("0001-01-01T00:00:00Z", FormatTime(start_time, 0));
+ EXPECT_EQ("9999-12-31T23:59:59Z", FormatTime(end_time, 0));
+
+ // Make sure the nanoseconds part is formated correctly.
+ EXPECT_EQ("1970-01-01T00:00:00.010Z", FormatTime(0, 10000000));
+ EXPECT_EQ("1970-01-01T00:00:00.000010Z", FormatTime(0, 10000));
+ EXPECT_EQ("1970-01-01T00:00:00.000000010Z", FormatTime(0, 10));
+}
+
+TEST(DateTimeTest, ParseString) {
+ int64 seconds;
+ int32 nanos;
+ ASSERT_TRUE(ParseTime("0001-01-01T00:00:00Z", &seconds, &nanos));
+ EXPECT_EQ("0001-01-01T00:00:00Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("9999-12-31T23:59:59.999999999Z", &seconds, &nanos));
+ EXPECT_EQ("9999-12-31T23:59:59.999999999Z", FormatTime(seconds, nanos));
+
+ // Test time zone offsets.
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00-08:00", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T08:00:00Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00+08:00", &seconds, &nanos));
+ EXPECT_EQ("1969-12-31T16:00:00Z", FormatTime(seconds, nanos));
+
+ // Test nanoseconds.
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.01Z", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T00:00:00.010Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.00001-08:00", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T08:00:00.000010Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.00000001+08:00", &seconds, &nanos));
+ EXPECT_EQ("1969-12-31T16:00:00.000000010Z", FormatTime(seconds, nanos));
+ // Fractional parts less than 1 nanosecond will be ignored.
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.0123456789Z", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T00:00:00.012345678Z", FormatTime(seconds, nanos));
+}
+
+} // namespace
+} // namespace internal
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc
index 61dfa5d6..4d8c1f91 100644
--- a/src/google/protobuf/text_format.cc
+++ b/src/google/protobuf/text_format.cc
@@ -1880,7 +1880,7 @@ void TextFormat::Printer::PrintUnknownFields(
generator.Print(field_number);
generator.Print(": 0x");
generator.Print(
- StrCat(strings::Hex(field.fixed32(), strings::Hex::ZERO_PAD_8)));
+ StrCat(strings::Hex(field.fixed32(), strings::ZERO_PAD_8)));
if (single_line_mode_) {
generator.Print(" ");
} else {
@@ -1892,7 +1892,7 @@ void TextFormat::Printer::PrintUnknownFields(
generator.Print(field_number);
generator.Print(": 0x");
generator.Print(
- StrCat(strings::Hex(field.fixed64(), strings::Hex::ZERO_PAD_16)));
+ StrCat(strings::Hex(field.fixed64(), strings::ZERO_PAD_16)));
if (single_line_mode_) {
generator.Print(" ");
} else {
diff --git a/src/google/protobuf/util/field_comparator.cc b/src/google/protobuf/util/field_comparator.cc
new file mode 100644
index 00000000..b7676a88
--- /dev/null
+++ b/src/google/protobuf/util/field_comparator.cc
@@ -0,0 +1,187 @@
+// 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: ksroka@google.com (Krzysztof Sroka)
+
+#include <google/protobuf/util/field_comparator.h>
+
+#include <string>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/mathlimits.h>
+#include <google/protobuf/stubs/mathutil.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+FieldComparator::FieldComparator() {}
+FieldComparator::~FieldComparator() {}
+
+DefaultFieldComparator::DefaultFieldComparator()
+ : float_comparison_(EXACT),
+ treat_nan_as_equal_(false),
+ has_default_tolerance_(false) {
+}
+
+DefaultFieldComparator::~DefaultFieldComparator() {}
+
+FieldComparator::ComparisonResult DefaultFieldComparator::Compare(
+ const google::protobuf::Message& message_1,
+ const google::protobuf::Message& message_2,
+ const google::protobuf::FieldDescriptor* field,
+ int index_1, int index_2,
+ const google::protobuf::util::FieldContext* field_context) {
+ const Reflection* reflection_1 = message_1.GetReflection();
+ const Reflection* reflection_2 = message_2.GetReflection();
+
+ switch (field->cpp_type()) {
+#define COMPARE_FIELD(METHOD) \
+ if (field->is_repeated()) { \
+ return ResultFromBoolean(Compare##METHOD( \
+ *field, \
+ reflection_1->GetRepeated##METHOD(message_1, field, index_1), \
+ reflection_2->GetRepeated##METHOD(message_2, field, index_2))); \
+ } else { \
+ return ResultFromBoolean(Compare##METHOD( \
+ *field, \
+ reflection_1->Get##METHOD(message_1, field), \
+ reflection_2->Get##METHOD(message_2, field))); \
+ } \
+ break; // Make sure no fall-through is introduced.
+
+ case FieldDescriptor::CPPTYPE_BOOL:
+ COMPARE_FIELD(Bool);
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ COMPARE_FIELD(Double);
+ case FieldDescriptor::CPPTYPE_ENUM:
+ COMPARE_FIELD(Enum);
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ COMPARE_FIELD(Float);
+ case FieldDescriptor::CPPTYPE_INT32:
+ COMPARE_FIELD(Int32);
+ case FieldDescriptor::CPPTYPE_INT64:
+ COMPARE_FIELD(Int64);
+ case FieldDescriptor::CPPTYPE_STRING:
+ COMPARE_FIELD(String);
+ case FieldDescriptor::CPPTYPE_UINT32:
+ COMPARE_FIELD(UInt32);
+ case FieldDescriptor::CPPTYPE_UINT64:
+ COMPARE_FIELD(UInt64);
+
+#undef COMPARE_FIELD
+
+ case FieldDescriptor::CPPTYPE_MESSAGE:
+ return RECURSE;
+
+ default:
+ GOOGLE_LOG(FATAL) << "No comparison code for field " << field->full_name()
+ << " of CppType = " << field->cpp_type();
+ }
+}
+
+void DefaultFieldComparator::SetDefaultFractionAndMargin(double fraction,
+ double margin) {
+ default_tolerance_ = Tolerance(fraction, margin);
+ has_default_tolerance_ = true;
+}
+
+void DefaultFieldComparator::SetFractionAndMargin(const FieldDescriptor* field,
+ double fraction,
+ double margin) {
+ GOOGLE_CHECK(FieldDescriptor::CPPTYPE_FLOAT == field->cpp_type() ||
+ FieldDescriptor::CPPTYPE_DOUBLE == field->cpp_type())
+ << "Field has to be float or double type. Field name is: "
+ << field->full_name();
+ map_tolerance_[field] = Tolerance(fraction, margin);
+}
+
+bool DefaultFieldComparator::CompareDouble(const FieldDescriptor& field,
+ double value_1, double value_2) {
+ return CompareDoubleOrFloat(field, value_1, value_2);
+}
+
+bool DefaultFieldComparator::CompareEnum(const FieldDescriptor& field,
+ const EnumValueDescriptor* value_1,
+ const EnumValueDescriptor* value_2) {
+ return value_1->number() == value_2->number();
+}
+
+bool DefaultFieldComparator::CompareFloat(const FieldDescriptor& field,
+ float value_1, float value_2) {
+ return CompareDoubleOrFloat(field, value_1, value_2);
+}
+
+template<typename T>
+bool DefaultFieldComparator::CompareDoubleOrFloat(const FieldDescriptor& field,
+ T value_1, T value_2) {
+ if (value_1 == value_2) {
+ // Covers +inf and -inf (which are not within margin or fraction of
+ // themselves), and is a shortcut for finite values.
+ return true;
+ } else if (float_comparison_ == EXACT) {
+ if (treat_nan_as_equal_ &&
+ MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) {
+ return true;
+ }
+ return false;
+ } else {
+ if (treat_nan_as_equal_ &&
+ MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) {
+ return true;
+ }
+ // float_comparison_ == APPROXIMATE covers two use cases.
+ Tolerance* tolerance = FindOrNull(map_tolerance_, &field);
+ if (tolerance == NULL && has_default_tolerance_) {
+ tolerance = &default_tolerance_;
+ }
+ if (tolerance == NULL) {
+ return MathUtil::AlmostEquals(value_1, value_2);
+ } else {
+ // Use user-provided fraction and margin. Since they are stored as
+ // doubles, we explicitely cast them to types of values provided. This
+ // is very likely to fail if provided values are not numeric.
+ return MathUtil::WithinFractionOrMargin(
+ value_1, value_2, static_cast<T>(tolerance->fraction),
+ static_cast<T>(tolerance->margin));
+ }
+ }
+}
+
+FieldComparator::ComparisonResult DefaultFieldComparator::ResultFromBoolean(
+ bool boolean_result) const {
+ return boolean_result ? FieldComparator::SAME : FieldComparator::DIFFERENT;
+}
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/field_comparator.h b/src/google/protobuf/util/field_comparator.h
new file mode 100644
index 00000000..ee676265
--- /dev/null
+++ b/src/google/protobuf/util/field_comparator.h
@@ -0,0 +1,259 @@
+// 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: ksroka@google.com (Krzysztof Sroka)
+
+#ifndef GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__
+#define GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__
+
+#include <map>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+class EnumValueDescriptor;
+class FieldDescriptor;
+
+namespace util {
+
+class FieldContext;
+
+// Base class specifying the interface for comparing protocol buffer fields.
+// Regular users should consider using or subclassing DefaultFieldComparator
+// rather than this interface.
+// Currently, this does not support comparing unknown fields.
+class LIBPROTOBUF_EXPORT FieldComparator {
+ public:
+ FieldComparator();
+ virtual ~FieldComparator();
+
+ enum ComparisonResult {
+ SAME, // Compared fields are equal. In case of comparing submessages,
+ // user should not recursively compare their contents.
+ DIFFERENT, // Compared fields are different. In case of comparing
+ // submessages, user should not recursively compare their
+ // contents.
+ RECURSE, // Compared submessages need to be compared recursively.
+ // FieldComparator does not specify the semantics of recursive
+ // comparison. This value should not be returned for simple
+ // values.
+ };
+
+ // Compares the values of a field in two protocol buffer messages.
+ // Returns SAME or DIFFERENT for simple values, and SAME, DIFFERENT or RECURSE
+ // for submessages. Returning RECURSE for fields not being submessages is
+ // illegal.
+ // In case the given FieldDescriptor points to a repeated field, the indices
+ // need to be valid. Otherwise they should be ignored.
+ //
+ // FieldContext contains information about the specific instances of the
+ // fields being compared, versus FieldDescriptor which only contains general
+ // type information about the fields.
+ virtual ComparisonResult Compare(
+ const google::protobuf::Message& message_1,
+ const google::protobuf::Message& message_2,
+ const google::protobuf::FieldDescriptor* field,
+ int index_1, int index_2,
+ const google::protobuf::util::FieldContext* field_context) = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldComparator);
+};
+
+// Basic implementation of FieldComparator. Supports four modes of floating
+// point value comparison: exact, approximate using MathUtil::AlmostEqual
+// method, and arbitrarilly precise using MathUtil::WithinFracionOrMargin.
+class LIBPROTOBUF_EXPORT DefaultFieldComparator : public FieldComparator {
+ public:
+ enum FloatComparison {
+ EXACT, // Floats and doubles are compared exactly.
+ APPROXIMATE, // Floats and doubles are compared using the
+ // MathUtil::AlmostEqual method or
+ // MathUtil::WithinFractionOrMargin method.
+ // TODO(ksroka): Introduce third value to differenciate uses of AlmostEqual
+ // and WithinFractionOrMargin.
+ };
+
+ // Creates new comparator with float comparison set to EXACT.
+ DefaultFieldComparator();
+
+ virtual ~DefaultFieldComparator();
+
+ virtual ComparisonResult Compare(
+ const google::protobuf::Message& message_1,
+ const google::protobuf::Message& message_2,
+ const google::protobuf::FieldDescriptor* field,
+ int index_1, int index_2,
+ const google::protobuf::util::FieldContext* field_context);
+
+ void set_float_comparison(FloatComparison float_comparison) {
+ float_comparison_ = float_comparison;
+ }
+
+ FloatComparison float_comparison() const {
+ return float_comparison_;
+ }
+
+ // Set whether the FieldComparator shall treat floats or doubles that are both
+ // NaN as equal (treat_nan_as_equal = true) or as different
+ // (treat_nan_as_equal = false). Default is treating NaNs always as different.
+ void set_treat_nan_as_equal(bool treat_nan_as_equal) {
+ treat_nan_as_equal_ = treat_nan_as_equal;
+ }
+
+ bool treat_nan_as_equal() const {
+ return treat_nan_as_equal_;
+ }
+
+ // Sets the fraction and margin for the float comparison of a given field.
+ // Uses MathUtil::WithinFractionOrMargin to compare the values.
+ //
+ // REQUIRES: field->cpp_type == FieldDescriptor::CPPTYPE_DOUBLE or
+ // field->cpp_type == FieldDescriptor::CPPTYPE_FLOAT
+ // REQUIRES: float_comparison_ == APPROXIMATE
+ void SetFractionAndMargin(const FieldDescriptor* field, double fraction,
+ double margin);
+
+ // Sets the fraction and margin for the float comparison of all float and
+ // double fields, unless a field has been given a specific setting via
+ // SetFractionAndMargin() above.
+ // Uses MathUtil::WithinFractionOrMargin to compare the values.
+ //
+ // REQUIRES: float_comparison_ == APPROXIMATE
+ void SetDefaultFractionAndMargin(double fraction, double margin);
+
+ private:
+ // Defines the tolerance for floating point comparison (fraction and margin).
+ struct Tolerance {
+ double fraction;
+ double margin;
+ Tolerance()
+ : fraction(0.0),
+ margin(0.0) {}
+ Tolerance(double f, double m)
+ : fraction(f),
+ margin(m) {}
+ };
+
+ // Defines the map to store the tolerances for floating point comparison.
+ typedef map<const FieldDescriptor*, Tolerance> ToleranceMap;
+
+ // The following methods get executed when CompareFields is called for the
+ // basic types (instead of submessages). They return true on success. One
+ // can use ResultFromBoolean() to convert that boolean to a ComparisonResult
+ // value.
+ bool CompareBool(const google::protobuf::FieldDescriptor& field,
+ bool value_1, bool value_2) {
+ return value_1 == value_2;
+ }
+
+ // Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and
+ // CompareFloat.
+ bool CompareDouble(const google::protobuf::FieldDescriptor& field,
+ double value_1, double value_2);
+
+ bool CompareEnum(const google::protobuf::FieldDescriptor& field,
+ const EnumValueDescriptor* value_1,
+ const EnumValueDescriptor* value_2);
+
+ // Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and
+ // CompareFloat.
+ bool CompareFloat(const google::protobuf::FieldDescriptor& field,
+ float value_1, float value_2);
+
+ bool CompareInt32(const google::protobuf::FieldDescriptor& field,
+ int32 value_1, int32 value_2) {
+ return value_1 == value_2;
+ }
+
+ bool CompareInt64(const google::protobuf::FieldDescriptor& field,
+ int64 value_1, int64 value_2) {
+ return value_1 == value_2;
+ }
+
+ bool CompareString(const google::protobuf::FieldDescriptor& field,
+ const string& value_1, const string& value_2) {
+ return value_1 == value_2;
+ }
+
+ bool CompareUInt32(const google::protobuf::FieldDescriptor& field,
+ uint32 value_1, uint32 value_2) {
+ return value_1 == value_2;
+ }
+
+ bool CompareUInt64(const google::protobuf::FieldDescriptor& field,
+ uint64 value_1, uint64 value_2) {
+ return value_1 == value_2;
+ }
+
+ // This function is used by CompareDouble and CompareFloat to avoid code
+ // duplication. There are no checks done against types of the values passed,
+ // but it's likely to fail if passed non-numeric arguments.
+ template<typename T>
+ bool CompareDoubleOrFloat(const google::protobuf::FieldDescriptor& field,
+ T value_1, T value_2);
+
+ // Returns FieldComparator::SAME if boolean_result is true and
+ // FieldComparator::DIFFERENT otherwise.
+ ComparisonResult ResultFromBoolean(bool boolean_result) const;
+
+ FloatComparison float_comparison_;
+
+ // If true, floats and doubles that are both NaN are considered to be
+ // equal. Otherwise, two floats or doubles that are NaN are considered to be
+ // different.
+ bool treat_nan_as_equal_;
+
+ // True iff default_tolerance_ has been explicitly set.
+ //
+ // If false, then the default tolerance for flaots and doubles is that which
+ // is used by MathUtil::AlmostEquals().
+ bool has_default_tolerance_;
+
+ // Default float/double tolerance. Only meaningful if
+ // has_default_tolerance_ == true.
+ Tolerance default_tolerance_;
+
+ // Field-specific float/double tolerances, which override any default for
+ // those particular fields.
+ ToleranceMap map_tolerance_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DefaultFieldComparator);
+};
+
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__
diff --git a/src/google/protobuf/util/field_comparator_test.cc b/src/google/protobuf/util/field_comparator_test.cc
new file mode 100644
index 00000000..748c7d11
--- /dev/null
+++ b/src/google/protobuf/util/field_comparator_test.cc
@@ -0,0 +1,483 @@
+// 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: ksroka@google.com (Krzysztof Sroka)
+
+#include <google/protobuf/util/field_comparator.h>
+
+#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <gtest/gtest.h>
+#include <google/protobuf/stubs/mathutil.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace {
+
+using protobuf_unittest::TestAllTypes;
+
+class DefaultFieldComparatorTest : public ::testing::Test {
+ protected:
+ void SetUp() {
+ descriptor_ = TestAllTypes::descriptor();
+ }
+
+ const Descriptor* descriptor_;
+ DefaultFieldComparator comparator_;
+ TestAllTypes message_1_;
+ TestAllTypes message_2_;
+};
+
+TEST_F(DefaultFieldComparatorTest, RecursesIntoGroup) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optionalgroup");
+ EXPECT_EQ(FieldComparator::RECURSE,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, RecursesIntoNestedMessage) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optional_nested_message");
+ EXPECT_EQ(FieldComparator::RECURSE,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, RecursesIntoForeignMessage) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optional_foreign_message");
+ EXPECT_EQ(FieldComparator::RECURSE,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, Int32Comparison) {
+ const FieldDescriptor* field = descriptor_->FindFieldByName("optional_int32");
+ message_1_.set_optional_int32(1);
+ message_2_.set_optional_int32(1);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+
+ message_2_.set_optional_int32(-1);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, Int64Comparison) {
+ const FieldDescriptor* field = descriptor_->FindFieldByName("optional_int64");
+ message_1_.set_optional_int64(1L);
+ message_2_.set_optional_int64(1L);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+
+ message_2_.set_optional_int64(-1L);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, UInt32Comparison) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optional_uint32");
+ message_1_.set_optional_uint32(1);
+ message_2_.set_optional_uint32(1);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+
+ message_2_.set_optional_uint32(2);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, UInt64Comparison) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optional_uint64");
+ message_1_.set_optional_uint64(1L);
+ message_2_.set_optional_uint64(1L);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+
+ message_2_.set_optional_uint64(2L);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, BooleanComparison) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optional_bool");
+ message_1_.set_optional_bool(true);
+ message_2_.set_optional_bool(true);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+
+ message_2_.set_optional_bool(false);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, EnumComparison) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optional_nested_enum");
+ message_1_.set_optional_nested_enum(TestAllTypes::BAR);
+ message_2_.set_optional_nested_enum(TestAllTypes::BAR);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+
+ message_2_.set_optional_nested_enum(TestAllTypes::BAZ);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, StringComparison) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("optional_string");
+ message_1_.set_optional_string("foo");
+ message_2_.set_optional_string("foo");
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+
+ message_2_.set_optional_string("bar");
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, FloatingPointComparisonExact) {
+ const FieldDescriptor* field_float =
+ descriptor_->FindFieldByName("optional_float");
+ const FieldDescriptor* field_double =
+ descriptor_->FindFieldByName("optional_double");
+
+ message_1_.set_optional_float(0.1f);
+ message_2_.set_optional_float(0.1f);
+ message_1_.set_optional_double(0.1);
+ message_2_.set_optional_double(0.1);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ message_2_.set_optional_float(0.2f);
+ message_2_.set_optional_double(0.2);
+
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, FloatingPointComparisonApproximate) {
+ const FieldDescriptor* field_float =
+ descriptor_->FindFieldByName("optional_float");
+ const FieldDescriptor* field_double =
+ descriptor_->FindFieldByName("optional_double");
+
+ message_1_.set_optional_float(2.300005f);
+ message_2_.set_optional_float(2.300006f);
+ message_1_.set_optional_double(2.3000000000000003);
+ message_2_.set_optional_double(2.3000000000000007);
+
+ // Approximate comparison depends on MathUtil, so we assert on MathUtil
+ // results first to check if that's where the failure was introduced.
+ ASSERT_NE(message_1_.optional_float(), message_2_.optional_float());
+ ASSERT_NE(message_1_.optional_double(), message_2_.optional_double());
+ ASSERT_TRUE(MathUtil::AlmostEquals(message_1_.optional_float(),
+ message_2_.optional_float()));
+ ASSERT_TRUE(MathUtil::AlmostEquals(message_1_.optional_double(),
+ message_2_.optional_double()));
+
+ // DefaultFieldComparator's default float comparison mode is EXACT.
+ ASSERT_EQ(DefaultFieldComparator::EXACT, comparator_.float_comparison());
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE);
+
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest, FloatingPointComparisonTreatNaNsAsEqual) {
+ const FieldDescriptor* field_float =
+ descriptor_->FindFieldByName("optional_float");
+ const FieldDescriptor* field_double =
+ descriptor_->FindFieldByName("optional_double");
+
+ message_1_.set_optional_float(MathLimits<float>::kNaN);
+ message_2_.set_optional_float(MathLimits<float>::kNaN);
+ message_1_.set_optional_double(MathLimits<double>::kNaN);
+ message_2_.set_optional_double(MathLimits<double>::kNaN);
+
+ // DefaultFieldComparator's default float comparison mode is EXACT with
+ // treating NaNs as different.
+ ASSERT_EQ(DefaultFieldComparator::EXACT, comparator_.float_comparison());
+ ASSERT_EQ(false, comparator_.treat_nan_as_equal());
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+ comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ comparator_.set_treat_nan_as_equal(true);
+ ASSERT_EQ(true, comparator_.treat_nan_as_equal());
+ comparator_.set_float_comparison(DefaultFieldComparator::EXACT);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+ comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest,
+ FloatingPointComparisonWithinFractionOrMargin) {
+ const FieldDescriptor* field_float =
+ descriptor_->FindFieldByName("optional_float");
+ const FieldDescriptor* field_double =
+ descriptor_->FindFieldByName("optional_double");
+
+ message_1_.set_optional_float(100.0f);
+ message_2_.set_optional_float(109.9f);
+ message_1_.set_optional_double(100.0);
+ message_2_.set_optional_double(109.9);
+
+ comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Should fail since the fraction is too low.
+ comparator_.SetFractionAndMargin(field_float, 0.01, 0.0);
+ comparator_.SetFractionAndMargin(field_double, 0.01, 0.0);
+
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Should fail since the margin is too low.
+ comparator_.SetFractionAndMargin(field_float, 0.0, 9.0);
+ comparator_.SetFractionAndMargin(field_double, 0.0, 9.0);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Should succeed since the fraction is high enough.
+ comparator_.SetFractionAndMargin(field_float, 0.2, 0.0);
+ comparator_.SetFractionAndMargin(field_double, 0.2, 0.0);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Should succeed since the margin is high enough.
+ comparator_.SetFractionAndMargin(field_float, 0.0, 10.0);
+ comparator_.SetFractionAndMargin(field_double, 0.0, 10.0);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Setting values for one of the fields should not affect the other.
+ comparator_.SetFractionAndMargin(field_double, 0.0, 0.0);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // +inf should be equal even though they are not technically within margin or
+ // fraction.
+ message_1_.set_optional_float(numeric_limits<float>::infinity());
+ message_2_.set_optional_float(numeric_limits<float>::infinity());
+ message_1_.set_optional_double(numeric_limits<double>::infinity());
+ message_2_.set_optional_double(numeric_limits<double>::infinity());
+ comparator_.SetFractionAndMargin(field_float, 0.0, 0.0);
+ comparator_.SetFractionAndMargin(field_double, 0.0, 0.0);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // -inf should be equal even though they are not technically within margin or
+ // fraction.
+ message_1_.set_optional_float(-numeric_limits<float>::infinity());
+ message_2_.set_optional_float(-numeric_limits<float>::infinity());
+ message_1_.set_optional_double(-numeric_limits<double>::infinity());
+ message_2_.set_optional_double(-numeric_limits<double>::infinity());
+ comparator_.SetFractionAndMargin(field_float, 0.0, 0.0);
+ comparator_.SetFractionAndMargin(field_double, 0.0, 0.0);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+}
+
+TEST_F(DefaultFieldComparatorTest,
+ FloatingPointComparisonWithinDefaultFractionOrMargin) {
+ const FieldDescriptor* field_float =
+ descriptor_->FindFieldByName("optional_float");
+ const FieldDescriptor* field_double =
+ descriptor_->FindFieldByName("optional_double");
+
+ message_1_.set_optional_float(100.0f);
+ message_2_.set_optional_float(109.9f);
+ message_1_.set_optional_double(100.0);
+ message_2_.set_optional_double(109.9);
+
+ comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE);
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Set default fraction and margin.
+ comparator_.SetDefaultFractionAndMargin(0.01, 0.0);
+
+ // Float comparisons should fail since the fraction is too low.
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Set field-specific fraction and margin for one field (field_float) but not
+ // the other (field_double)
+ comparator_.SetFractionAndMargin(field_float, 0.2, 0.0);
+
+ // The field with the override should succeed, since its field-specific
+ // fraction is high enough.
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ // The field with no override should fail, since the default fraction is too
+ // low
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // Set the default fraction and margin high enough so that fields that use
+ // the default should succeed
+ comparator_.SetDefaultFractionAndMargin(0.2, 0.0);
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+
+ // The field with an override should still be OK
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+
+ // Set fraction and margin for the field with an override to be too low
+ comparator_.SetFractionAndMargin(field_float, 0.01, 0.0);
+
+ // Now our default is high enough but field_float's override is too low.
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_,
+ field_float, -1, -1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_,
+ field_double, -1, -1, NULL));
+}
+
+// Simple test checking whether we compare values at correct indices.
+TEST_F(DefaultFieldComparatorTest, RepeatedFieldComparison) {
+ const FieldDescriptor* field =
+ descriptor_->FindFieldByName("repeated_string");
+
+ message_1_.add_repeated_string("foo");
+ message_1_.add_repeated_string("bar");
+ message_2_.add_repeated_string("bar");
+ message_2_.add_repeated_string("baz");
+
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, 0, 0, NULL));
+ EXPECT_EQ(FieldComparator::DIFFERENT,
+ comparator_.Compare(message_1_, message_2_, field, 1, 1, NULL));
+ EXPECT_EQ(FieldComparator::SAME,
+ comparator_.Compare(message_1_, message_2_, field, 1, 0, NULL));
+}
+
+} // namespace util
+} // namespace protobuf
+} // namespace
+} // namespace google
diff --git a/src/google/protobuf/util/internal/constants.h b/src/google/protobuf/util/internal/constants.h
new file mode 100644
index 00000000..0cb8f6e1
--- /dev/null
+++ b/src/google/protobuf/util/internal/constants.h
@@ -0,0 +1,93 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_CONSTANTS_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_CONSTANTS_H__
+
+#include <google/protobuf/stubs/common.h>
+
+// This file contains constants used by //net/proto2/util/converter.
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+// Prefix for type URLs.
+const char kTypeServiceBaseUrl[] = "type.googleapis.com";
+
+// Format string for RFC3339 timestamp formatting.
+const char kRfc3339TimeFormat[] = "%Y-%m-%dT%H:%M:%S";
+
+// Minimum seconds allowed in a google.protobuf.TimeStamp or Duration value.
+const int64 kMinSeconds = -315576000000;
+
+// Maximum seconds allowed in a google.protobuf.TimeStamp or Duration value.
+const int64 kMaxSeconds = 315576000000;
+
+// Nano seconds in a second.
+const int32 kNanosPerSecond = 1000000000;
+
+// Type url representing NULL values in google.protobuf.Struct type.
+const char kStructNullValueTypeUrl[] =
+ "type.googleapis.com/google.protobuf.NullValue";
+
+// Type string for google.protobuf.Struct
+const char kStructType[] = "google.protobuf.Struct";
+
+// Type string for struct.proto's google.protobuf.Value value type.
+const char kStructValueType[] = "google.protobuf.Value";
+
+// Type string for struct.proto's google.protobuf.ListValue value type.
+const char kStructListValueType[] = "google.protobuf.ListValue";
+
+// Type string for google.protobuf.Timestamp
+const char kTimestampType[] = "google.protobuf.Timestamp";
+
+// Type string for google.protobuf.Duration
+const char kDurationType[] = "google.protobuf.Duration";
+
+// Type URL for struct value type google.protobuf.Value
+const char kStructValueTypeUrl[] = "type.googleapis.com/google.protobuf.Value";
+
+// Type URL for struct value type google.protobuf.Value
+const char kStructTypeUrl[] = "type.googleapis.com/google.protobuf.Struct";
+
+// Type string for google.protobuf.Any
+const char kAnyType[] = "google.protobuf.Any";
+
+// The type URL of google.protobuf.FieldMask;
+const char kFieldMaskTypeUrl[] =
+ "type.googleapis.com/google.protobuf.FieldMask";
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_CONSTANTS_H__
diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc
new file mode 100644
index 00000000..72b737e9
--- /dev/null
+++ b/src/google/protobuf/util/internal/datapiece.cc
@@ -0,0 +1,285 @@
+// 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 <google/protobuf/util/internal/datapiece.h>
+
+#include <google/protobuf/struct.pb.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/mathutil.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using google::protobuf::EnumDescriptor;
+using google::protobuf::EnumValueDescriptor;
+;
+;
+using util::error::Code;
+using util::Status;
+using util::StatusOr;
+
+namespace {
+
+inline Status InvalidArgument(StringPiece value_str) {
+ return Status(util::error::INVALID_ARGUMENT, value_str);
+}
+
+// For general conversion between
+// int32, int64, uint32, uint64, double and float
+// except conversion between double and float.
+template <typename To, typename From>
+StatusOr<To> NumberConvertAndCheck(From before) {
+ if (::google::protobuf::internal::is_same<From, To>::value) return before;
+ To after = static_cast<To>(before);
+ if (after == before &&
+ MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) {
+ return after;
+ } else {
+ return InvalidArgument(::google::protobuf::internal::is_integral<From>::value
+ ? ValueAsString(before)
+ : ::google::protobuf::internal::is_same<From, double>::value
+ ? DoubleAsString(before)
+ : FloatAsString(before));
+ }
+}
+
+// For conversion between double and float only.
+template <typename To, typename From>
+StatusOr<To> FloatingPointConvertAndCheck(From before) {
+ if (isnan(before)) return std::numeric_limits<To>::quiet_NaN();
+
+ To after = static_cast<To>(before);
+ if (MathUtil::AlmostEquals<To>(after, before)) {
+ return after;
+ } else {
+ return InvalidArgument(::google::protobuf::internal::is_same<From, double>::value
+ ? DoubleAsString(before)
+ : FloatAsString(before));
+ }
+}
+
+} // namespace
+
+StatusOr<int32> DataPiece::ToInt32() const {
+ if (type_ == TYPE_STRING) {
+ return StringToNumber<int32>(safe_strto32);
+ }
+ return GenericConvert<int32>();
+}
+
+StatusOr<uint32> DataPiece::ToUint32() const {
+ if (type_ == TYPE_STRING) {
+ return StringToNumber<uint32>(safe_strtou32);
+ }
+ return GenericConvert<uint32>();
+}
+
+StatusOr<int64> DataPiece::ToInt64() const {
+ if (type_ == TYPE_STRING) {
+ return StringToNumber<int64>(safe_strto64);
+ }
+ return GenericConvert<int64>();
+}
+
+StatusOr<uint64> DataPiece::ToUint64() const {
+ if (type_ == TYPE_STRING) {
+ return StringToNumber<uint64>(safe_strtou64);
+ }
+ return GenericConvert<uint64>();
+}
+
+StatusOr<double> DataPiece::ToDouble() const {
+ if (type_ == TYPE_FLOAT) {
+ return FloatingPointConvertAndCheck<double, float>(float_);
+ }
+ if (type_ == TYPE_STRING) {
+ if (str_ == "Infinity") return std::numeric_limits<double>::infinity();
+ if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity();
+ if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN();
+ return StringToNumber<double>(safe_strtod);
+ }
+ return GenericConvert<double>();
+}
+
+StatusOr<float> DataPiece::ToFloat() const {
+ if (type_ == TYPE_DOUBLE) {
+ return FloatingPointConvertAndCheck<float, double>(double_);
+ }
+ if (type_ == TYPE_STRING) {
+ if (str_ == "Infinity") return std::numeric_limits<float>::infinity();
+ if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity();
+ if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN();
+ // SafeStrToFloat() is used instead of safe_strtof() because the later
+ // does not fail on inputs like SimpleDtoa(DBL_MAX).
+ return StringToNumber<float>(SafeStrToFloat);
+ }
+ return GenericConvert<float>();
+}
+
+StatusOr<bool> DataPiece::ToBool() const {
+ switch (type_) {
+ case TYPE_BOOL:
+ return bool_;
+ case TYPE_STRING:
+ return StringToNumber<bool>(safe_strtob);
+ default:
+ return InvalidArgument(
+ ValueAsStringOrDefault("Wrong type. Cannot convert to Bool."));
+ }
+}
+
+StatusOr<string> DataPiece::ToString() const {
+ switch (type_) {
+ case TYPE_STRING:
+ return str_.ToString();
+ case TYPE_BYTES: {
+ string base64;
+ WebSafeBase64Escape(str_, &base64);
+ return base64;
+ }
+ default:
+ return InvalidArgument(
+ ValueAsStringOrDefault("Cannot convert to string."));
+ }
+}
+
+string DataPiece::ValueAsStringOrDefault(StringPiece default_string) const {
+ switch (type_) {
+ case TYPE_INT32:
+ return SimpleItoa(i32_);
+ case TYPE_INT64:
+ return SimpleItoa(i64_);
+ case TYPE_UINT32:
+ return SimpleItoa(u32_);
+ case TYPE_UINT64:
+ return SimpleItoa(u64_);
+ case TYPE_DOUBLE:
+ return DoubleAsString(double_);
+ case TYPE_FLOAT:
+ return FloatAsString(float_);
+ case TYPE_BOOL:
+ return SimpleBtoa(bool_);
+ case TYPE_STRING:
+ return StrCat("\"", str_.ToString(), "\"");
+ case TYPE_BYTES: {
+ string base64;
+ WebSafeBase64Escape(str_, &base64);
+ return StrCat("\"", base64, "\"");
+ }
+ case TYPE_NULL:
+ return "null";
+ default:
+ return default_string.ToString();
+ }
+}
+
+StatusOr<string> DataPiece::ToBytes() const {
+ if (type_ == TYPE_BYTES) return str_.ToString();
+ if (type_ == TYPE_STRING) {
+ string decoded;
+ if (!WebSafeBase64Unescape(str_, &decoded)) {
+ if (!Base64Unescape(str_, &decoded)) {
+ return InvalidArgument(
+ ValueAsStringOrDefault("Invalid data in input."));
+ }
+ }
+ return decoded;
+ } else {
+ return InvalidArgument(ValueAsStringOrDefault(
+ "Wrong type. Only String or Bytes can be converted to Bytes."));
+ }
+}
+
+StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type) const {
+ if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE;
+
+ if (type_ == TYPE_STRING) {
+ // First try the given value as a name.
+ string enum_name = str_.ToString();
+ const google::protobuf::EnumValue* value =
+ FindEnumValueByNameOrNull(enum_type, enum_name);
+ if (value != NULL) return value->number();
+ // Next try a normalized name.
+ for (string::iterator it = enum_name.begin(); it != enum_name.end(); ++it) {
+ *it = *it == '-' ? '_' : ascii_toupper(*it);
+ }
+ value = FindEnumValueByNameOrNull(enum_type, enum_name);
+ if (value != NULL) return value->number();
+ } else {
+ StatusOr<int32> value = ToInt32();
+ if (value.ok()) {
+ if (const google::protobuf::EnumValue* enum_value =
+ FindEnumValueByNumberOrNull(enum_type, value.ValueOrDie())) {
+ return enum_value->number();
+ }
+ }
+ }
+ return InvalidArgument(
+ ValueAsStringOrDefault("Cannot find enum with given value."));
+}
+
+template <typename To>
+StatusOr<To> DataPiece::GenericConvert() const {
+ switch (type_) {
+ case TYPE_INT32:
+ return NumberConvertAndCheck<To, int32>(i32_);
+ case TYPE_INT64:
+ return NumberConvertAndCheck<To, int64>(i64_);
+ case TYPE_UINT32:
+ return NumberConvertAndCheck<To, uint32>(u32_);
+ case TYPE_UINT64:
+ return NumberConvertAndCheck<To, uint64>(u64_);
+ case TYPE_DOUBLE:
+ return NumberConvertAndCheck<To, double>(double_);
+ case TYPE_FLOAT:
+ return NumberConvertAndCheck<To, float>(float_);
+ default: // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL
+ return InvalidArgument(ValueAsStringOrDefault(
+ "Wrong type. Bool, Enum, String and Cord not supported in "
+ "GenericConvert."));
+ }
+}
+
+template <typename To>
+StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece, To*)) const {
+ To result;
+ if (func(str_, &result)) return result;
+ return InvalidArgument(StrCat("\"", str_.ToString(), "\""));
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/datapiece.h b/src/google/protobuf/util/internal/datapiece.h
new file mode 100644
index 00000000..30947252
--- /dev/null
+++ b/src/google/protobuf/util/internal/datapiece.h
@@ -0,0 +1,212 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/statusor.h>
+
+
+namespace google {
+namespace protobuf {
+class Enum;
+} // namespace protobuf
+
+
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// Container for a single piece of data together with its data type.
+//
+// For primitive types (int32, int64, uint32, uint64, double, float, bool),
+// the data is stored by value.
+//
+// For string, a StringPiece is stored. For Cord, a pointer to Cord is stored.
+// Just like StringPiece, the DataPiece class does not own the storage for
+// the actual string or Cord, so it is the user's responsiblity to guarantee
+// that the underlying storage is still valid when the DataPiece is accessed.
+class LIBPROTOBUF_EXPORT DataPiece {
+ public:
+ // Identifies data type of the value.
+ // These are the types supported by DataPiece.
+ enum Type {
+ TYPE_INT32 = 1,
+ TYPE_INT64 = 2,
+ TYPE_UINT32 = 3,
+ TYPE_UINT64 = 4,
+ TYPE_DOUBLE = 5,
+ TYPE_FLOAT = 6,
+ TYPE_BOOL = 7,
+ TYPE_ENUM = 8,
+ TYPE_STRING = 9,
+ TYPE_BYTES = 10,
+ TYPE_NULL = 11, // explicit NULL type
+ };
+
+ // Constructors and Destructor
+ explicit DataPiece(const int32 value) : type_(TYPE_INT32), i32_(value) {}
+ explicit DataPiece(const int64 value) : type_(TYPE_INT64), i64_(value) {}
+ explicit DataPiece(const uint32 value) : type_(TYPE_UINT32), u32_(value) {}
+ explicit DataPiece(const uint64 value) : type_(TYPE_UINT64), u64_(value) {}
+ explicit DataPiece(const double value) : type_(TYPE_DOUBLE), double_(value) {}
+ explicit DataPiece(const float value) : type_(TYPE_FLOAT), float_(value) {}
+ explicit DataPiece(const bool value) : type_(TYPE_BOOL), bool_(value) {}
+ explicit DataPiece(StringPiece value)
+ : type_(TYPE_STRING),
+ str_(StringPiecePod::CreateFromStringPiece(value)) {}
+ // Constructor for bytes. The second parameter is not used.
+ explicit DataPiece(StringPiece value, bool dummy)
+ : type_(TYPE_BYTES), str_(StringPiecePod::CreateFromStringPiece(value)) {}
+ DataPiece(const DataPiece& r) : type_(r.type_), str_(r.str_) {}
+ DataPiece& operator=(const DataPiece& x) {
+ type_ = x.type_;
+ str_ = x.str_;
+ return *this;
+ }
+
+ static DataPiece NullData() { return DataPiece(TYPE_NULL, 0); }
+
+ virtual ~DataPiece() {}
+
+ // Accessors
+ Type type() const { return type_; }
+
+ StringPiece str() const {
+ GOOGLE_LOG_IF(DFATAL, type_ != TYPE_STRING) << "Not a string type.";
+ return str_;
+ }
+
+
+ // Parses, casts or converts the value stored in the DataPiece into an int32.
+ util::StatusOr<int32> ToInt32() const;
+
+ // Parses, casts or converts the value stored in the DataPiece into a uint32.
+ util::StatusOr<uint32> ToUint32() const;
+
+ // Parses, casts or converts the value stored in the DataPiece into an int64.
+ util::StatusOr<int64> ToInt64() const;
+
+ // Parses, casts or converts the value stored in the DataPiece into a uint64.
+ util::StatusOr<uint64> ToUint64() const;
+
+ // Parses, casts or converts the value stored in the DataPiece into a double.
+ util::StatusOr<double> ToDouble() const;
+
+ // Parses, casts or converts the value stored in the DataPiece into a float.
+ util::StatusOr<float> ToFloat() const;
+
+ // Parses, casts or converts the value stored in the DataPiece into a bool.
+ util::StatusOr<bool> ToBool() const;
+
+ // Parses, casts or converts the value stored in the DataPiece into a string.
+ util::StatusOr<string> ToString() const;
+
+ // Tries to convert the value contained in this datapiece to string. If the
+ // conversion fails, it returns the default_string.
+ string ValueAsStringOrDefault(StringPiece default_string) const;
+
+ util::StatusOr<string> ToBytes() const;
+
+ // Converts a value into protocol buffer enum number. If the value is a
+ // string, first attempts conversion by name, trying names as follows:
+ // 1) the directly provided string value.
+ // 2) the value upper-cased and replacing '-' by '_'
+ // If the value is not a string, attempts to convert to a 32-bit integer.
+ // If none of these succeeds, returns a conversion error status.
+ util::StatusOr<int> ToEnum(const google::protobuf::Enum* enum_type) const;
+
+ private:
+ // Disallow implicit constructor.
+ DataPiece();
+
+ // Helper to create NULL or ENUM types.
+ DataPiece(Type type, int32 val) : type_(type), i32_(val) {}
+
+ // For numeric conversion between
+ // int32, int64, uint32, uint64, double, float and bool
+ template <typename To>
+ util::StatusOr<To> GenericConvert() const;
+
+ // For conversion from string to
+ // int32, int64, uint32, uint64, double, float and bool
+ template <typename To>
+ util::StatusOr<To> StringToNumber(bool (*func)(StringPiece, To*)) const;
+
+ // Data type for this piece of data.
+ Type type_;
+
+ // StringPiece is not a POD and can not be used in an union (pre C++11). We
+ // need a POD version of it.
+ struct StringPiecePod {
+ const char* data;
+ int size;
+
+ // Create from a StringPiece.
+ static StringPiecePod CreateFromStringPiece(StringPiece str) {
+ StringPiecePod pod;
+ pod.data = str.data();
+ pod.size = str.size();
+ return pod;
+ }
+
+ // Cast to StringPiece.
+ operator StringPiece() const { return StringPiece(data, size); }
+
+ bool operator==(const char* value) const {
+ return StringPiece(data, size) == StringPiece(value);
+ }
+
+ string ToString() const { return string(data, size); }
+ };
+
+ // Stored piece of data.
+ union {
+ const int32 i32_;
+ const int64 i64_;
+ const uint32 u32_;
+ const uint64 u64_;
+ const double double_;
+ const float float_;
+ const bool bool_;
+ StringPiecePod str_;
+ };
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc
new file mode 100644
index 00000000..267e2cd3
--- /dev/null
+++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc
@@ -0,0 +1,515 @@
+// 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 <google/protobuf/util/internal/default_value_objectwriter.h>
+
+#include <google/protobuf/stubs/hash.h>
+
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/stubs/map_util.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+using util::Status;
+using util::StatusOr;
+namespace converter {
+
+DefaultValueObjectWriter::DefaultValueObjectWriter(
+ TypeResolver* type_resolver, const google::protobuf::Type& type,
+ ObjectWriter* ow)
+ : typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+ type_(type),
+ disable_normalize_(false),
+ current_(NULL),
+ root_(NULL),
+ ow_(ow) {}
+
+DefaultValueObjectWriter::~DefaultValueObjectWriter() {
+ for (int i = 0; i < string_values_.size(); ++i) {
+ delete string_values_[i];
+ }
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(StringPiece name,
+ bool value) {
+ if (current_ == NULL) {
+ ow_->RenderBool(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt32(
+ StringPiece name, int32 value) {
+ if (current_ == NULL) {
+ ow_->RenderInt32(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32(
+ StringPiece name, uint32 value) {
+ if (current_ == NULL) {
+ ow_->RenderUint32(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64(
+ StringPiece name, int64 value) {
+ if (current_ == NULL) {
+ ow_->RenderInt64(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64(
+ StringPiece name, uint64 value) {
+ if (current_ == NULL) {
+ ow_->RenderUint64(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderDouble(
+ StringPiece name, double value) {
+ if (current_ == NULL) {
+ ow_->RenderDouble(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderFloat(
+ StringPiece name, float value) {
+ if (current_ == NULL) {
+ ow_->RenderBool(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString(
+ StringPiece name, StringPiece value) {
+ if (current_ == NULL) {
+ ow_->RenderString(name, value);
+ } else {
+ // Since StringPiece is essentially a pointer, takes a copy of "value" to
+ // avoid ownership issues.
+ string_values_.push_back(new string(value.ToString()));
+ RenderDataPiece(name, DataPiece(*string_values_.back()));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes(
+ StringPiece name, StringPiece value) {
+ if (current_ == NULL) {
+ ow_->RenderBytes(name, value);
+ } else {
+ RenderDataPiece(name, DataPiece(value));
+ }
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull(
+ StringPiece name) {
+ if (current_ == NULL) {
+ ow_->RenderNull(name);
+ } else {
+ RenderDataPiece(name, DataPiece::NullData());
+ }
+ return this;
+}
+
+DefaultValueObjectWriter*
+DefaultValueObjectWriter::DisableCaseNormalizationForNextKey() {
+ disable_normalize_ = true;
+ return this;
+}
+
+DefaultValueObjectWriter::Node::Node(const string& name,
+ const google::protobuf::Type* type,
+ NodeKind kind, const DataPiece& data,
+ bool is_placeholder)
+ : name_(name),
+ type_(type),
+ kind_(kind),
+ disable_normalize_(false),
+ is_any_(false),
+ data_(data),
+ is_placeholder_(is_placeholder) {}
+
+DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild(
+ StringPiece name) {
+ if (name.empty() || kind_ != OBJECT) {
+ return NULL;
+ }
+ for (int i = 0; i < children_.size(); ++i) {
+ Node* child = children_[i];
+ if (child->name() == name) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) {
+ if (disable_normalize_) {
+ ow->DisableCaseNormalizationForNextKey();
+ }
+ if (kind_ == PRIMITIVE) {
+ ObjectWriter::RenderDataPieceTo(data_, name_, ow);
+ return;
+ }
+ if (is_placeholder_) {
+ // If is_placeholder_ = true, we didn't see this node in the response, so
+ // skip output.
+ return;
+ }
+ if (kind_ == LIST) {
+ ow->StartList(name_);
+ } else {
+ ow->StartObject(name_);
+ }
+ for (int i = 0; i < children_.size(); ++i) {
+ Node* child = children_[i];
+ child->WriteTo(ow);
+ }
+ if (kind_ == LIST) {
+ ow->EndList();
+ } else {
+ ow->EndObject();
+ }
+}
+
+const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType(
+ const google::protobuf::Type& found_type, TypeInfo* typeinfo) {
+ // If this field is a map, we should use the type of its "Value" as
+ // the type of the child node.
+ for (int i = 0; i < found_type.fields_size(); ++i) {
+ const google::protobuf::Field& sub_field = found_type.fields(i);
+ if (sub_field.number() != 2) {
+ continue;
+ }
+ if (sub_field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ // This map's value type is not a message type. We don't need to
+ // get the field_type in this case.
+ break;
+ }
+ util::StatusOr<const google::protobuf::Type*> sub_type =
+ typeinfo->ResolveTypeUrl(sub_field.type_url());
+ if (!sub_type.ok()) {
+ GOOGLE_LOG(WARNING) << "Cannot resolve type '" << sub_field.type_url() << "'.";
+ } else {
+ return sub_type.ValueOrDie();
+ }
+ break;
+ }
+ return NULL;
+}
+
+void DefaultValueObjectWriter::Node::PopulateChildren(TypeInfo* typeinfo) {
+ // Ignores well known types that don't require automatically populating their
+ // primitive children. For type "Any", we only populate its children when the
+ // "@type" field is set.
+ // TODO(tsun): remove "kStructValueType" from the list. It's being checked
+ // now because of a bug in the tool-chain that causes the "oneof_index"
+ // of kStructValueType to not be set correctly.
+ if (type_ == NULL || type_->name() == kAnyType ||
+ type_->name() == kStructType || type_->name() == kTimestampType ||
+ type_->name() == kDurationType || type_->name() == kStructValueType) {
+ return;
+ }
+ std::vector<Node*> new_children;
+ hash_map<string, int> orig_children_map;
+
+ // Creates a map of child nodes to speed up lookup.
+ for (int i = 0; i < children_.size(); ++i) {
+ InsertIfNotPresent(&orig_children_map, children_[i]->name_, i);
+ }
+
+ for (int i = 0; i < type_->fields_size(); ++i) {
+ const google::protobuf::Field& field = type_->fields(i);
+ hash_map<string, int>::iterator found =
+ orig_children_map.find(field.name());
+ // If the child field has already been set, we just add it to the new list
+ // of children.
+ if (found != orig_children_map.end()) {
+ new_children.push_back(children_[found->second]);
+ children_[found->second] = NULL;
+ continue;
+ }
+
+ const google::protobuf::Type* field_type = NULL;
+ bool is_map = false;
+ NodeKind kind = PRIMITIVE;
+
+ if (field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ kind = OBJECT;
+ util::StatusOr<const google::protobuf::Type*> found_result =
+ typeinfo->ResolveTypeUrl(field.type_url());
+ if (!found_result.ok()) {
+ // "field" is of an unknown type.
+ GOOGLE_LOG(WARNING) << "Cannot resolve type '" << field.type_url() << "'.";
+ } else {
+ const google::protobuf::Type* found_type = found_result.ValueOrDie();
+ is_map = IsMap(field, *found_type);
+
+ if (!is_map) {
+ field_type = found_type;
+ } else {
+ // If this field is a map, we should use the type of its "Value" as
+ // the type of the child node.
+ field_type = GetMapValueType(*found_type, typeinfo);
+ kind = MAP;
+ }
+ }
+ }
+ if (!is_map &&
+ field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
+ kind = LIST;
+ }
+ // If the child field is of primitive type, sets its data to the default
+ // value of its type.
+ // If oneof_index() != 0, the child field is part of a "oneof", which means
+ // the child field is optional and we shouldn't populate its default value.
+ google::protobuf::scoped_ptr<Node> child(
+ new Node(field.name(), field_type, kind,
+ ((kind == PRIMITIVE && field.oneof_index() == 0)
+ ? CreateDefaultDataPieceForField(field)
+ : DataPiece::NullData()),
+ true));
+ new_children.push_back(child.release());
+ }
+ // Adds all leftover nodes in children_ to the beginning of new_child.
+ for (int i = 0; i < children_.size(); ++i) {
+ if (children_[i] == NULL) {
+ continue;
+ }
+ new_children.insert(new_children.begin(), children_[i]);
+ children_[i] = NULL;
+ }
+ children_.swap(new_children);
+}
+
+void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) {
+ // If this is an "Any" node with "@type" already given and no other children
+ // have been added, populates its children.
+ if (node != NULL && node->is_any() && node->type() != NULL &&
+ node->type()->name() != kAnyType && node->number_of_children() == 1) {
+ node->PopulateChildren(typeinfo_.get());
+ }
+}
+
+DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField(
+ const google::protobuf::Field& field) {
+ switch (field.kind()) {
+ case google::protobuf::Field_Kind_TYPE_DOUBLE: {
+ return DataPiece(static_cast<double>(0));
+ }
+ case google::protobuf::Field_Kind_TYPE_FLOAT: {
+ return DataPiece(static_cast<float>(0));
+ }
+ case google::protobuf::Field_Kind_TYPE_INT64:
+ case google::protobuf::Field_Kind_TYPE_SINT64:
+ case google::protobuf::Field_Kind_TYPE_SFIXED64: {
+ return DataPiece(static_cast<int64>(0));
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT64:
+ case google::protobuf::Field_Kind_TYPE_FIXED64: {
+ return DataPiece(static_cast<uint64>(0));
+ }
+ case google::protobuf::Field_Kind_TYPE_INT32:
+ case google::protobuf::Field_Kind_TYPE_SINT32:
+ case google::protobuf::Field_Kind_TYPE_SFIXED32: {
+ return DataPiece(static_cast<int32>(0));
+ }
+ case google::protobuf::Field_Kind_TYPE_BOOL: {
+ return DataPiece(false);
+ }
+ case google::protobuf::Field_Kind_TYPE_STRING: {
+ return DataPiece(string());
+ }
+ case google::protobuf::Field_Kind_TYPE_BYTES: {
+ return DataPiece("", false);
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT32:
+ case google::protobuf::Field_Kind_TYPE_FIXED32: {
+ return DataPiece(static_cast<uint32>(0));
+ }
+ default: { return DataPiece::NullData(); }
+ }
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject(
+ StringPiece name) {
+ if (current_ == NULL) {
+ root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(),
+ false));
+ root_->set_disable_normalize(GetAndResetDisableNormalize());
+ root_->PopulateChildren(typeinfo_.get());
+ current_ = root_.get();
+ return this;
+ }
+ MaybePopulateChildrenOfAny(current_);
+ Node* child = current_->FindChild(name);
+ if (current_->kind() == LIST || current_->kind() == MAP || child == NULL) {
+ // If current_ is a list or a map node, we should create a new child and use
+ // the type of current_ as the type of the new child.
+ google::protobuf::scoped_ptr<Node> node(new Node(
+ name.ToString(), ((current_->kind() == LIST || current_->kind() == MAP)
+ ? current_->type()
+ : NULL),
+ OBJECT, DataPiece::NullData(), false));
+ child = node.get();
+ current_->AddChild(node.release());
+ }
+
+ child->set_is_placeholder(false);
+ child->set_disable_normalize(GetAndResetDisableNormalize());
+ if (child->kind() == OBJECT && child->number_of_children() == 0) {
+ child->PopulateChildren(typeinfo_.get());
+ }
+
+ stack_.push(current_);
+ current_ = child;
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::EndObject() {
+ if (stack_.empty()) {
+ // The root object ends here. Writes out the tree.
+ WriteRoot();
+ return this;
+ }
+ current_ = stack_.top();
+ stack_.pop();
+ return this;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::StartList(
+ StringPiece name) {
+ if (current_ == NULL) {
+ root_.reset(
+ new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false));
+ root_->set_disable_normalize(GetAndResetDisableNormalize());
+ current_ = root_.get();
+ return this;
+ }
+ MaybePopulateChildrenOfAny(current_);
+ Node* child = current_->FindChild(name);
+ if (child == NULL || child->kind() != LIST) {
+ GOOGLE_LOG(WARNING) << "Cannot find field '" << name << "'.";
+ google::protobuf::scoped_ptr<Node> node(
+ new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false));
+ child = node.get();
+ current_->AddChild(node.release());
+ }
+ child->set_is_placeholder(false);
+ child->set_disable_normalize(GetAndResetDisableNormalize());
+
+ stack_.push(current_);
+ current_ = child;
+ return this;
+}
+
+void DefaultValueObjectWriter::WriteRoot() {
+ root_->WriteTo(ow_);
+ root_.reset(NULL);
+ current_ = NULL;
+}
+
+DefaultValueObjectWriter* DefaultValueObjectWriter::EndList() {
+ if (stack_.empty()) {
+ WriteRoot();
+ return this;
+ }
+ current_ = stack_.top();
+ stack_.pop();
+ return this;
+}
+
+void DefaultValueObjectWriter::RenderDataPiece(StringPiece name,
+ const DataPiece& data) {
+ MaybePopulateChildrenOfAny(current_);
+ util::StatusOr<string> data_string = data.ToString();
+ if (current_->type() != NULL && current_->type()->name() == kAnyType &&
+ name == "@type" && data_string.ok()) {
+ const string& string_value = data_string.ValueOrDie();
+ // If the type of current_ is "Any" and its "@type" field is being set here,
+ // sets the type of current_ to be the type specified by the "@type".
+ util::StatusOr<const google::protobuf::Type*> found_type =
+ typeinfo_->ResolveTypeUrl(string_value);
+ if (!found_type.ok()) {
+ GOOGLE_LOG(WARNING) << "Failed to resolve type '" << string_value << "'.";
+ } else {
+ current_->set_type(found_type.ValueOrDie());
+ }
+ current_->set_is_any(true);
+ // If the "@type" field is placed after other fields, we should populate
+ // other children of primitive type now. Otherwise, we should wait until the
+ // first value field is rendered before we populate the children, because
+ // the "value" field of a Any message could be omitted.
+ if (current_->number_of_children() > 1 && current_->type() != NULL) {
+ current_->PopulateChildren(typeinfo_.get());
+ }
+ }
+ Node* child = current_->FindChild(name);
+ if (child == NULL || child->kind() != PRIMITIVE) {
+ GOOGLE_LOG(WARNING) << "Cannot find primitive field '" << name << "'.";
+ // No children are found, creates a new child.
+ google::protobuf::scoped_ptr<Node> node(
+ new Node(name.ToString(), NULL, PRIMITIVE, data, false));
+ child = node.get();
+ current_->AddChild(node.release());
+ } else {
+ child->set_data(data);
+ }
+ child->set_disable_normalize(GetAndResetDisableNormalize());
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h
new file mode 100644
index 00000000..759ba91b
--- /dev/null
+++ b/src/google/protobuf/util/internal/default_value_objectwriter.h
@@ -0,0 +1,238 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_DEFAULT_VALUE_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_DEFAULT_VALUE_OBJECTWRITER_H__
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <stack>
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/internal/datapiece.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// An ObjectWriter that renders non-repeated primitive fields of proto messages
+// with their default values. DefaultValueObjectWriter holds objects, lists and
+// fields it receives in a tree structure and writes them out to another
+// ObjectWriter when EndObject() is called on the root object. It also writes
+// out all non-repeated primitive fields that haven't been explicitly rendered
+// with their default values (0 for numbers, "" for strings, etc).
+class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter {
+ public:
+ DefaultValueObjectWriter(TypeResolver* type_resolver,
+ const google::protobuf::Type& type,
+ ObjectWriter* ow);
+
+ virtual ~DefaultValueObjectWriter();
+
+ // ObjectWriter methods.
+ virtual DefaultValueObjectWriter* StartObject(StringPiece name);
+
+ virtual DefaultValueObjectWriter* EndObject();
+
+ virtual DefaultValueObjectWriter* StartList(StringPiece name);
+
+ virtual DefaultValueObjectWriter* EndList();
+
+ virtual DefaultValueObjectWriter* RenderBool(StringPiece name, bool value);
+
+ virtual DefaultValueObjectWriter* RenderInt32(StringPiece name, int32 value);
+
+ virtual DefaultValueObjectWriter* RenderUint32(StringPiece name,
+ uint32 value);
+
+ virtual DefaultValueObjectWriter* RenderInt64(StringPiece name, int64 value);
+
+ virtual DefaultValueObjectWriter* RenderUint64(StringPiece name,
+ uint64 value);
+
+ virtual DefaultValueObjectWriter* RenderDouble(StringPiece name,
+ double value);
+
+ virtual DefaultValueObjectWriter* RenderFloat(StringPiece name, float value);
+
+ virtual DefaultValueObjectWriter* RenderString(StringPiece name,
+ StringPiece value);
+ virtual DefaultValueObjectWriter* RenderBytes(StringPiece name,
+ StringPiece value);
+
+ virtual DefaultValueObjectWriter* RenderNull(StringPiece name);
+
+ virtual DefaultValueObjectWriter* DisableCaseNormalizationForNextKey();
+
+ private:
+ enum NodeKind {
+ PRIMITIVE = 0,
+ OBJECT = 1,
+ LIST = 2,
+ MAP = 3,
+ };
+
+ // "Node" represents a node in the tree that holds the input of
+ // DefaultValueObjectWriter.
+ class Node {
+ public:
+ Node(const string& name, const google::protobuf::Type* type, NodeKind kind,
+ const DataPiece& data, bool is_placeholder);
+ virtual ~Node() {
+ for (int i = 0; i < children_.size(); ++i) {
+ delete children_[i];
+ }
+ }
+
+ // Adds a child to this node. Takes ownership of this child.
+ void AddChild(Node* child) { children_.push_back(child); }
+
+ // Finds the child given its name.
+ Node* FindChild(StringPiece name);
+
+ // Populates children of this Node based on its type. If there are already
+ // children created, they will be merged to the result. Caller should pass
+ // in TypeInfo for looking up types of the children.
+ void PopulateChildren(TypeInfo* typeinfo);
+
+ // If this node is a leaf (has data), writes the current node to the
+ // ObjectWriter; if not, then recursively writes the children to the
+ // ObjectWriter.
+ void WriteTo(ObjectWriter* ow);
+
+ // Accessors
+ const string& name() const { return name_; }
+
+ const google::protobuf::Type* type() { return type_; }
+
+ void set_type(const google::protobuf::Type* type) { type_ = type; }
+
+ NodeKind kind() { return kind_; }
+
+ int number_of_children() { return children_.size(); }
+
+ void set_data(const DataPiece& data) { data_ = data; }
+
+ void set_disable_normalize(bool disable_normalize) {
+ disable_normalize_ = disable_normalize;
+ }
+
+ bool is_any() { return is_any_; }
+
+ void set_is_any(bool is_any) { is_any_ = is_any; }
+
+ void set_is_placeholder(bool is_placeholder) {
+ is_placeholder_ = is_placeholder;
+ }
+
+ private:
+ // Returns the Value Type of a map given the Type of the map entry and a
+ // TypeInfo instance.
+ const google::protobuf::Type* GetMapValueType(
+ const google::protobuf::Type& entry_type, TypeInfo* typeinfo);
+
+ // The name of this node.
+ string name_;
+ // google::protobuf::Type of this node. Owned by TypeInfo.
+ const google::protobuf::Type* type_;
+ // The kind of this node.
+ NodeKind kind_;
+ // Whether to disable case normalization of the name.
+ bool disable_normalize_;
+ // Whether this is a node for "Any".
+ bool is_any_;
+ // The data of this node when it is a leaf node.
+ DataPiece data_;
+ // Children of this node.
+ std::vector<Node*> children_;
+ // Whether this node is a placeholder for an object or list automatically
+ // generated when creating the parent node. Should be set to false after
+ // the parent node's StartObject()/StartList() method is called with this
+ // node's name.
+ bool is_placeholder_;
+ };
+
+ // Populates children of "node" if it is an "any" Node and its real type has
+ // been given.
+ void MaybePopulateChildrenOfAny(Node* node);
+
+ // Writes the root_ node to ow_ and resets the root_ and current_ pointer to
+ // NULL.
+ void WriteRoot();
+
+ // Creates a DataPiece containing the default value of the type of the field.
+ static DataPiece CreateDefaultDataPieceForField(
+ const google::protobuf::Field& field);
+
+ // Returns disable_normalize_ and reset it to false.
+ bool GetAndResetDisableNormalize() {
+ return disable_normalize_ ? (disable_normalize_ = false, true) : false;
+ }
+
+ // Adds or replaces the data_ of a primitive child node.
+ void RenderDataPiece(StringPiece name, const DataPiece& data);
+
+ // Type information for all the types used in the descriptor. Used to find
+ // google::protobuf::Type of nested messages/enums.
+ google::protobuf::scoped_ptr<TypeInfo> typeinfo_;
+ // google::protobuf::Type of the root message type.
+ const google::protobuf::Type& type_;
+ // Holds copies of strings passed to RenderString.
+ vector<string*> string_values_;
+
+ // Whether to disable case normalization of the next node.
+ bool disable_normalize_;
+ // The current Node. Owned by its parents.
+ Node* current_;
+ // The root Node.
+ google::protobuf::scoped_ptr<Node> root_;
+ // The stack to hold the path of Nodes from current_ to root_;
+ std::stack<Node*> stack_;
+
+ ObjectWriter* ow_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DefaultValueObjectWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_DEFAULT_VALUE_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
new file mode 100644
index 00000000..593c7105
--- /dev/null
+++ b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc
@@ -0,0 +1,139 @@
+// 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 <google/protobuf/util/internal/default_value_objectwriter.h>
+#include <google/protobuf/util/internal/expecting_objectwriter.h>
+#include <google/protobuf/util/internal/testdata/default_value_test.pb.h>
+#include <google/protobuf/util/internal/type_info_test_helper.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+namespace testing {
+
+using google::protobuf::testing::DefaultValueTest;
+
+// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are
+// in the marshalling_test.cc and translator_integration_test.cc.
+class DefaultValueObjectWriterTest
+ : public ::testing::TestWithParam<testing::TypeInfoSource> {
+ protected:
+ DefaultValueObjectWriterTest()
+ : helper_(GetParam()), mock_(), expects_(&mock_) {
+ helper_.ResetTypeInfo(DefaultValueTest::descriptor());
+ testing_.reset(helper_.NewDefaultValueWriter(
+ string(kTypeServiceBaseUrl) + "/" +
+ DefaultValueTest::descriptor()->full_name(),
+ &mock_));
+ }
+
+ virtual ~DefaultValueObjectWriterTest() {}
+
+ TypeInfoTestHelper helper_;
+ MockObjectWriter mock_;
+ ExpectingObjectWriter expects_;
+ google::protobuf::scoped_ptr<DefaultValueObjectWriter> testing_;
+};
+
+INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
+ DefaultValueObjectWriterTest,
+ ::testing::Values(
+ testing::USE_TYPE_RESOLVER));
+
+TEST_P(DefaultValueObjectWriterTest, Empty) {
+ // Set expectation
+ expects_.StartObject("")
+ ->RenderDouble("double_value", 0.0)
+ ->RenderFloat("float_value", 0.0)
+ ->RenderInt64("int64_value", 0)
+ ->RenderUint64("uint64_value", 0)
+ ->RenderInt32("int32_value", 0)
+ ->RenderUint32("uint32_value", 0)
+ ->RenderBool("bool_value", false)
+ ->RenderString("string_value", "")
+ ->RenderBytes("bytes_value", "")
+ ->EndObject();
+
+ // Actual testing
+ testing_->StartObject("")->EndObject();
+}
+
+TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) {
+ // Set expectation
+ expects_.StartObject("")
+ ->RenderDouble("double_value", 1.0)
+ ->RenderFloat("float_value", 0.0)
+ ->RenderInt64("int64_value", 0)
+ ->RenderUint64("uint64_value", 0)
+ ->RenderInt32("int32_value", 0)
+ ->RenderUint32("uint32_value", 0)
+ ->RenderBool("bool_value", false)
+ ->RenderString("string_value", "")
+ ->EndObject();
+
+ // Actual testing
+ testing_->StartObject("")->RenderDouble("double_value", 1.0)->EndObject();
+}
+
+TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) {
+ // Set expectation
+ expects_.StartObject("")
+ ->RenderDouble("double_value", 1.0)
+ ->RenderFloat("float_value", 0.0)
+ ->RenderInt64("int64_value", 0)
+ ->RenderUint64("uint64_value", 0)
+ ->RenderInt32("int32_value", 0)
+ ->RenderUint32("uint32_value", 0)
+ ->RenderBool("bool_value", false)
+ ->RenderString("string_value", "")
+ ->RenderString("unknown", "abc")
+ ->StartObject("unknown_object")
+ ->RenderString("unknown", "def")
+ ->EndObject()
+ ->EndObject();
+
+ // Actual testing
+ testing_->StartObject("")
+ ->RenderDouble("double_value", 1.0)
+ ->RenderString("unknown", "abc")
+ ->StartObject("unknown_object")
+ ->RenderString("unknown", "def")
+ ->EndObject()
+ ->EndObject();
+}
+
+} // namespace testing
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/error_listener.cc b/src/google/protobuf/util/internal/error_listener.cc
new file mode 100644
index 00000000..538307ba
--- /dev/null
+++ b/src/google/protobuf/util/internal/error_listener.cc
@@ -0,0 +1,42 @@
+// 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 <google/protobuf/util/internal/error_listener.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/error_listener.h b/src/google/protobuf/util/internal/error_listener.h
new file mode 100644
index 00000000..9b907df5
--- /dev/null
+++ b/src/google/protobuf/util/internal/error_listener.h
@@ -0,0 +1,99 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/location_tracker.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// Interface for error listener.
+class LIBPROTOBUF_EXPORT ErrorListener {
+ public:
+ virtual ~ErrorListener() {}
+
+ // Reports an invalid name at the given location.
+ virtual void InvalidName(const LocationTrackerInterface& loc,
+ StringPiece unknown_name, StringPiece message) = 0;
+
+ // Reports an invalid value for a field.
+ virtual void InvalidValue(const LocationTrackerInterface& loc,
+ StringPiece type_name, StringPiece value) = 0;
+
+ // Reports a missing required field.
+ virtual void MissingField(const LocationTrackerInterface& loc,
+ StringPiece missing_name) = 0;
+
+ protected:
+ ErrorListener() {}
+
+ private:
+ // Do not add any data members to this class.
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorListener);
+};
+
+// An error listener that ignores all errors.
+class LIBPROTOBUF_EXPORT NoopErrorListener : public ErrorListener {
+ public:
+ NoopErrorListener() {}
+ virtual ~NoopErrorListener() {}
+
+ virtual void InvalidName(const LocationTrackerInterface& loc,
+ StringPiece unknown_name, StringPiece message) {}
+
+ virtual void InvalidValue(const LocationTrackerInterface& loc,
+ StringPiece type_name, StringPiece value) {}
+
+ virtual void MissingField(const LocationTrackerInterface& loc,
+ StringPiece missing_name) {}
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(NoopErrorListener);
+};
+
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
diff --git a/src/google/protobuf/util/internal/expecting_objectwriter.h b/src/google/protobuf/util/internal/expecting_objectwriter.h
new file mode 100644
index 00000000..ae98ddd8
--- /dev/null
+++ b/src/google/protobuf/util/internal/expecting_objectwriter.h
@@ -0,0 +1,238 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__
+
+// An implementation of ObjectWriter that automatically sets the
+// gmock expectations for the response to a method. Every method
+// returns the object itself for chaining.
+//
+// Usage:
+// // Setup
+// MockObjectWriter mock;
+// ExpectingObjectWriter ow(&mock);
+//
+// // Set expectation
+// ow.StartObject("")
+// ->RenderString("key", "value")
+// ->EndObject();
+//
+// // Actual testing
+// mock.StartObject(StringPiece())
+// ->RenderString("key", "value")
+// ->EndObject();
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <gmock/gmock.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using testing::IsEmpty;
+using testing::NanSensitiveDoubleEq;
+using testing::NanSensitiveFloatEq;
+using testing::Return;
+using testing::StrEq;
+using testing::TypedEq;
+
+class MockObjectWriter : public ObjectWriter {
+ public:
+ MockObjectWriter() {}
+
+ MOCK_METHOD1(StartObject, ObjectWriter*(StringPiece));
+ MOCK_METHOD0(EndObject, ObjectWriter*());
+ MOCK_METHOD1(StartList, ObjectWriter*(StringPiece));
+ MOCK_METHOD0(EndList, ObjectWriter*());
+ MOCK_METHOD2(RenderBool, ObjectWriter*(StringPiece, bool));
+ MOCK_METHOD2(RenderInt32, ObjectWriter*(StringPiece, int32));
+ MOCK_METHOD2(RenderUint32, ObjectWriter*(StringPiece, uint32));
+ MOCK_METHOD2(RenderInt64, ObjectWriter*(StringPiece, int64));
+ MOCK_METHOD2(RenderUint64, ObjectWriter*(StringPiece, uint64));
+ MOCK_METHOD2(RenderDouble, ObjectWriter*(StringPiece, double));
+ MOCK_METHOD2(RenderFloat, ObjectWriter*(StringPiece, float));
+ MOCK_METHOD2(RenderString, ObjectWriter*(StringPiece, StringPiece));
+ MOCK_METHOD2(RenderBytes, ObjectWriter*(StringPiece, StringPiece));
+ MOCK_METHOD1(RenderNull, ObjectWriter*(StringPiece));
+};
+
+class ExpectingObjectWriter : public ObjectWriter {
+ public:
+ explicit ExpectingObjectWriter(MockObjectWriter* mock) : mock_(mock) {}
+
+ virtual ObjectWriter* StartObject(StringPiece name) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, StartObject(IsEmpty()))
+ : EXPECT_CALL(*mock_, StartObject(StrEq(name.ToString()))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* EndObject() {
+ EXPECT_CALL(*mock_, EndObject())
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* StartList(StringPiece name) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, StartList(IsEmpty()))
+ : EXPECT_CALL(*mock_, StartList(StrEq(name.ToString()))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* EndList() {
+ EXPECT_CALL(*mock_, EndList())
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderBool(StringPiece name, bool value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderBool(IsEmpty(), TypedEq<bool>(value)))
+ : EXPECT_CALL(*mock_, RenderBool(StrEq(name.ToString()),
+ TypedEq<bool>(value))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderInt32(StringPiece name, int32 value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderInt32(IsEmpty(), TypedEq<int32>(value)))
+ : EXPECT_CALL(*mock_, RenderInt32(StrEq(name.ToString()),
+ TypedEq<int32>(value))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderUint32(StringPiece name, uint32 value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderUint32(IsEmpty(), TypedEq<uint32>(value)))
+ : EXPECT_CALL(*mock_, RenderUint32(StrEq(name.ToString()),
+ TypedEq<uint32>(value))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderInt64(StringPiece name, int64 value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderInt64(IsEmpty(), TypedEq<int64>(value)))
+ : EXPECT_CALL(*mock_, RenderInt64(StrEq(name.ToString()),
+ TypedEq<int64>(value))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderUint64(StringPiece name, uint64 value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderUint64(IsEmpty(), TypedEq<uint64>(value)))
+ : EXPECT_CALL(*mock_, RenderUint64(StrEq(name.ToString()),
+ TypedEq<uint64>(value))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderDouble(StringPiece name, double value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderDouble(IsEmpty(),
+ NanSensitiveDoubleEq(value)))
+ : EXPECT_CALL(*mock_, RenderDouble(StrEq(name.ToString()),
+ NanSensitiveDoubleEq(value))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderFloat(StringPiece name, float value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderFloat(IsEmpty(),
+ NanSensitiveFloatEq(value)))
+ : EXPECT_CALL(*mock_, RenderFloat(StrEq(name.ToString()),
+ NanSensitiveFloatEq(value))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderString(StringPiece name, StringPiece value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderString(IsEmpty(),
+ TypedEq<StringPiece>(value.ToString())))
+ : EXPECT_CALL(*mock_, RenderString(StrEq(name.ToString()),
+ TypedEq<StringPiece>(value.ToString()))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+ virtual ObjectWriter* RenderBytes(StringPiece name, StringPiece value) {
+ (name.empty()
+ ? EXPECT_CALL(*mock_, RenderBytes(IsEmpty(), TypedEq<StringPiece>(
+ value.ToString())))
+ : EXPECT_CALL(*mock_,
+ RenderBytes(StrEq(name.ToString()),
+ TypedEq<StringPiece>(value.ToString()))))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation();
+ return this;
+ }
+
+ virtual ObjectWriter* RenderNull(StringPiece name) {
+ (name.empty() ? EXPECT_CALL(*mock_, RenderNull(IsEmpty()))
+ : EXPECT_CALL(*mock_, RenderNull(StrEq(name.ToString())))
+ .WillOnce(Return(mock_))
+ .RetiresOnSaturation());
+ return this;
+ }
+
+ private:
+ MockObjectWriter* mock_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ExpectingObjectWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/field_mask_utility.cc b/src/google/protobuf/util/internal/field_mask_utility.cc
new file mode 100644
index 00000000..92468959
--- /dev/null
+++ b/src/google/protobuf/util/internal/field_mask_utility.cc
@@ -0,0 +1,228 @@
+// 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 <google/protobuf/util/internal/field_mask_utility.h>
+
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status_macros.h>
+
+namespace google {
+
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+inline util::Status CallPathSink(PathSinkCallback path_sink,
+ StringPiece arg) {
+ return path_sink->Run(arg);
+}
+
+util::Status CreatePublicError(util::error::Code code,
+ const string& message) {
+ return util::Status(code, message);
+}
+
+// Appends a FieldMask path segment to a prefix.
+string AppendPathSegmentToPrefix(StringPiece prefix, StringPiece segment) {
+ if (prefix.empty()) {
+ return segment.ToString();
+ }
+ if (segment.empty()) {
+ return prefix.ToString();
+ }
+ // If the segment is a map key, appends it to the prefix without the ".".
+ if (segment.starts_with("[\"")) {
+ return StrCat(prefix, segment);
+ }
+ return StrCat(prefix, ".", segment);
+}
+
+} // namespace
+
+string ConvertFieldMaskPath(const StringPiece path,
+ ConverterCallback converter) {
+ string result;
+ result.reserve(path.size() << 1);
+
+ bool is_quoted = false;
+ bool is_escaping = false;
+ int current_segment_start = 0;
+
+ // Loops until 1 passed the end of the input to make handling the last
+ // segment easier.
+ for (size_t i = 0; i <= path.size(); ++i) {
+ // Outputs quoted string as-is.
+ if (is_quoted) {
+ if (i == path.size()) {
+ break;
+ }
+ result.push_back(path[i]);
+ if (is_escaping) {
+ is_escaping = false;
+ } else if (path[i] == '\\') {
+ is_escaping = true;
+ } else if (path[i] == '\"') {
+ current_segment_start = i + 1;
+ is_quoted = false;
+ }
+ continue;
+ }
+ if (i == path.size() || path[i] == '.' || path[i] == '(' ||
+ path[i] == ')' || path[i] == '\"') {
+ result += converter(
+ path.substr(current_segment_start, i - current_segment_start));
+ if (i < path.size()) {
+ result.push_back(path[i]);
+ }
+ current_segment_start = i + 1;
+ }
+ if (i < path.size() && path[i] == '\"') {
+ is_quoted = true;
+ }
+ }
+ return result;
+}
+
+util::Status DecodeCompactFieldMaskPaths(StringPiece paths,
+ PathSinkCallback path_sink) {
+ stack<string> prefix;
+ int length = paths.length();
+ int previous_position = 0;
+ bool in_map_key = false;
+ bool is_escaping = false;
+ // Loops until 1 passed the end of the input to make the handle of the last
+ // segment easier.
+ for (int i = 0; i <= length; ++i) {
+ if (i != length) {
+ // Skips everything in a map key until we hit the end of it, which is
+ // marked by an un-escaped '"' immediately followed by a ']'.
+ if (in_map_key) {
+ if (is_escaping) {
+ is_escaping = false;
+ continue;
+ }
+ if (paths[i] == '\\') {
+ is_escaping = true;
+ continue;
+ }
+ if (paths[i] != '\"') {
+ continue;
+ }
+ // Un-escaped '"' must be followed with a ']'.
+ if (i >= length - 1 || paths[i + 1] != ']') {
+ return CreatePublicError(
+ util::error::INVALID_ARGUMENT,
+ StrCat("Invalid FieldMask '", paths,
+ "'. Map keys should be represented as [\"some_key\"]."));
+ }
+ // The end of the map key ("\"]") has been found.
+ in_map_key = false;
+ // Skips ']'.
+ i++;
+ // Checks whether the key ends at the end of a path segment.
+ if (i < length - 1 && paths[i + 1] != '.' && paths[i + 1] != ',' &&
+ paths[i + 1] != ')' && paths[i + 1] != '(') {
+ return CreatePublicError(
+ util::error::INVALID_ARGUMENT,
+ StrCat("Invalid FieldMask '", paths,
+ "'. Map keys should be at the end of a path segment."));
+ }
+ is_escaping = false;
+ continue;
+ }
+
+ // We are not in a map key, look for the start of one.
+ if (paths[i] == '[') {
+ if (i >= length - 1 || paths[i + 1] != '\"') {
+ return CreatePublicError(
+ util::error::INVALID_ARGUMENT,
+ StrCat("Invalid FieldMask '", paths,
+ "'. Map keys should be represented as [\"some_key\"]."));
+ }
+ // "[\"" starts a map key.
+ in_map_key = true;
+ i++; // Skips the '\"'.
+ continue;
+ }
+ // If the current character is not a special character (',', '(' or ')'),
+ // continue to the next.
+ if (paths[i] != ',' && paths[i] != ')' && paths[i] != '(') {
+ continue;
+ }
+ }
+ // Gets the current segment - sub-string between previous position (after
+ // '(', ')', ',', or the beginning of the input) and the current position.
+ StringPiece segment =
+ paths.substr(previous_position, i - previous_position);
+ string current_prefix = prefix.empty() ? "" : prefix.top();
+
+ if (i < length && paths[i] == '(') {
+ // Builds a prefix and save it into the stack.
+ prefix.push(AppendPathSegmentToPrefix(current_prefix, segment));
+ } else if (!segment.empty()) {
+ // When the current charactor is ')', ',' or the current position has
+ // passed the end of the input, builds and outputs a new paths by
+ // concatenating the last prefix with the current segment.
+ RETURN_IF_ERROR(CallPathSink(
+ path_sink, AppendPathSegmentToPrefix(current_prefix, segment)));
+ }
+
+ // Removes the last prefix after seeing a ')'.
+ if (i < length && paths[i] == ')') {
+ if (prefix.empty()) {
+ return CreatePublicError(
+ util::error::INVALID_ARGUMENT,
+ StrCat("Invalid FieldMask '", paths,
+ "'. Cannot find matching '(' for all ')'."));
+ }
+ prefix.pop();
+ }
+ previous_position = i + 1;
+ }
+ if (in_map_key) {
+ return CreatePublicError(
+ util::error::INVALID_ARGUMENT,
+ StrCat("Invalid FieldMask '", paths,
+ "'. Cannot find matching ']' for all '['."));
+ }
+ if (!prefix.empty()) {
+ return CreatePublicError(
+ util::error::INVALID_ARGUMENT,
+ StrCat("Invalid FieldMask '", paths,
+ "'. Cannot find matching ')' for all '('."));
+ }
+ return util::Status::OK;
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/field_mask_utility.h b/src/google/protobuf/util/internal/field_mask_utility.h
new file mode 100644
index 00000000..59f36f75
--- /dev/null
+++ b/src/google/protobuf/util/internal/field_mask_utility.h
@@ -0,0 +1,72 @@
+// 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.
+
+// FieldMask related utility methods.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_FIELD_MASK_UTILITY_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_FIELD_MASK_UTILITY_H__
+
+#include <functional>
+#include <stack>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+typedef string (*ConverterCallback)(StringPiece);
+typedef ResultCallback1<util::Status, StringPiece>* PathSinkCallback;
+
+// Applies a 'converter' to each segment of a FieldMask path and returns the
+// result. Quoted strings in the 'path' are copied to the output as-is without
+// converting their content. Escaping is supported within quoted strings.
+// For example, "ab\"_c" will be returned as "ab\"_c" without any changes.
+string ConvertFieldMaskPath(const StringPiece path,
+ ConverterCallback converter);
+
+// Decodes a compact list of FieldMasks. For example, "a.b,a.c.d,a.c.e" will be
+// decoded into a list of field paths - "a.b", "a.c.d", "a.c.e". And the results
+// will be sent to 'path_sink', i.e. 'path_sink' will be called once per
+// resulting path.
+// Note that we also support Apiary style FieldMask form. The above example in
+// the Apiary style will look like "a.b,a.c(d,e)".
+util::Status DecodeCompactFieldMaskPaths(StringPiece paths,
+ PathSinkCallback path_sink);
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_FIELD_MASK_UTILITY_H__
diff --git a/src/google/protobuf/util/internal/json_escaping.cc b/src/google/protobuf/util/internal/json_escaping.cc
new file mode 100644
index 00000000..5ac23421
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_escaping.cc
@@ -0,0 +1,403 @@
+// 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 <google/protobuf/util/internal/json_escaping.h>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+
+// Array of hex characters for conversion to hex.
+static const char kHex[] = "0123456789abcdef";
+
+// Characters 0x00 to 0x9f are very commonly used, so we provide a special
+// table lookup.
+//
+// For unicode code point ch < 0xa0:
+// kCommonEscapes[ch] is the escaped string of ch, if escaping is needed;
+// or an empty string, if escaping is not needed.
+static const char kCommonEscapes[160][7] = {
+ // C0 (ASCII and derivatives) control characters
+ "\\u0000", "\\u0001", "\\u0002", "\\u0003", // 0x00
+ "\\u0004", "\\u0005", "\\u0006", "\\u0007",
+ "\\b", "\\t", "\\n", "\\u000b",
+ "\\f", "\\r", "\\u000e", "\\u000f",
+ "\\u0010", "\\u0011", "\\u0012", "\\u0013", // 0x10
+ "\\u0014", "\\u0015", "\\u0016", "\\u0017",
+ "\\u0018", "\\u0019", "\\u001a", "\\u001b",
+ "\\u001c", "\\u001d", "\\u001e", "\\u001f",
+ // Escaping of " and \ are required by www.json.org string definition.
+ // Escaping of < and > are required for HTML security.
+ "", "", "\\\"", "", "", "", "", "", // 0x20
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", // 0x30
+ "", "", "", "", "\\u003c", "", "\\u003e", "",
+ "", "", "", "", "", "", "", "", // 0x40
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", // 0x50
+ "", "", "", "", "\\\\", "", "", "",
+ "", "", "", "", "", "", "", "", // 0x60
+ "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", // 0x70
+ "", "", "", "", "", "", "", "\\u007f",
+ // C1 (ISO 8859 and Unicode) extended control characters
+ "\\u0080", "\\u0081", "\\u0082", "\\u0083", // 0x80
+ "\\u0084", "\\u0085", "\\u0086", "\\u0087",
+ "\\u0088", "\\u0089", "\\u008a", "\\u008b",
+ "\\u008c", "\\u008d", "\\u008e", "\\u008f",
+ "\\u0090", "\\u0091", "\\u0092", "\\u0093", // 0x90
+ "\\u0094", "\\u0095", "\\u0096", "\\u0097",
+ "\\u0098", "\\u0099", "\\u009a", "\\u009b",
+ "\\u009c", "\\u009d", "\\u009e", "\\u009f"
+};
+
+// Determines if the given char value is a unicode high-surrogate code unit.
+// Such values do not represent characters by themselves, but are used in the
+// representation of supplementary characters in the utf-16 encoding.
+inline bool IsHighSurrogate(uint16 c) {
+ // Optimized form of:
+ // return c >= kMinHighSurrogate && c <= kMaxHighSurrogate;
+ // (Reduced from 3 ALU instructions to 2 ALU instructions)
+ return (c & ~(JsonEscaping::kMaxHighSurrogate -
+ JsonEscaping::kMinHighSurrogate))
+ == JsonEscaping::kMinHighSurrogate;
+}
+
+// Determines if the given char value is a unicode low-surrogate code unit.
+// Such values do not represent characters by themselves, but are used in the
+// representation of supplementary characters in the utf-16 encoding.
+inline bool IsLowSurrogate(uint16 c) {
+ // Optimized form of:
+ // return c >= kMinLowSurrogate && c <= kMaxLowSurrogate;
+ // (Reduced from 3 ALU instructions to 2 ALU instructions)
+ return (c & ~(JsonEscaping::kMaxLowSurrogate -
+ JsonEscaping::kMinLowSurrogate))
+ == JsonEscaping::kMinLowSurrogate;
+}
+
+// Determines if the given char value is a unicode surrogate code unit (either
+// high-surrogate or low-surrogate).
+inline bool IsSurrogate(uint32 c) {
+ // Optimized form of:
+ // return c >= kMinHighSurrogate && c <= kMaxLowSurrogate;
+ // (Reduced from 3 ALU instructions to 2 ALU instructions)
+ return (c & 0xfffff800) == JsonEscaping::kMinHighSurrogate;
+}
+
+// Returns true if the given unicode code point cp is
+// in the supplementary character range.
+inline bool IsSupplementalCodePoint(uint32 cp) {
+ // Optimized form of:
+ // return kMinSupplementaryCodePoint <= cp && cp <= kMaxCodePoint;
+ // (Reduced from 3 ALU instructions to 2 ALU instructions)
+ return (cp & ~(JsonEscaping::kMinSupplementaryCodePoint - 1))
+ < JsonEscaping::kMaxCodePoint;
+}
+
+// Returns true if the given unicode code point cp is a valid
+// unicode code point (i.e. in the range 0 <= cp <= kMaxCodePoint).
+inline bool IsValidCodePoint(uint32 cp) {
+ return cp <= JsonEscaping::kMaxCodePoint;
+}
+
+// Converts the specified surrogate pair to its supplementary code point value.
+// It is the callers' responsibility to validate the specified surrogate pair.
+inline uint32 ToCodePoint(uint16 high, uint16 low) {
+ // Optimized form of:
+ // return ((high - kMinHighSurrogate) << 10)
+ // + (low - kMinLowSurrogate)
+ // + kMinSupplementaryCodePoint;
+ // (Reduced from 5 ALU instructions to 3 ALU instructions)
+ return (high << 10) + low +
+ (JsonEscaping::kMinSupplementaryCodePoint
+ - (static_cast<unsigned>(JsonEscaping::kMinHighSurrogate) << 10)
+ - JsonEscaping::kMinLowSurrogate);
+}
+
+// Returns the low surrogate for the given unicode code point. The result is
+// meaningless if the given code point is not a supplementary character.
+inline uint16 ToLowSurrogate(uint32 cp) {
+ return (cp & (JsonEscaping::kMaxLowSurrogate
+ - JsonEscaping::kMinLowSurrogate))
+ + JsonEscaping::kMinLowSurrogate;
+}
+
+// Returns the high surrogate for the given unicode code point. The result is
+// meaningless if the given code point is not a supplementary character.
+inline uint16 ToHighSurrogate(uint32 cp) {
+ return (cp >> 10) + (JsonEscaping::kMinHighSurrogate -
+ (JsonEscaping::kMinSupplementaryCodePoint >> 10));
+}
+
+// Input str is encoded in UTF-8. A unicode code point could be encoded in
+// UTF-8 using anywhere from 1 to 4 characters, and it could span multiple
+// reads of the ByteSource.
+//
+// This function reads the next unicode code point from the input (str) at
+// the given position (index), taking into account any left-over partial
+// code point from the previous iteration (cp), together with the number
+// of characters left to read to complete this code point (num_left).
+//
+// This function assumes that the input (str) is valid at the given position
+// (index). In order words, at least one character could be read successfully.
+//
+// The code point read (partial or complete) is stored in (cp). Upon return,
+// (num_left) stores the number of characters that has yet to be read in
+// order to complete the current unicode code point. If the read is complete,
+// then (num_left) is 0. Also, (num_read) is the number of characters read.
+//
+// Returns false if we encounter an invalid UTF-8 string. Returns true
+// otherwise, including the case when we reach the end of the input (str)
+// before a complete unicode code point is read.
+bool ReadCodePoint(StringPiece str, int index,
+ uint32 *cp, int* num_left, int *num_read) {
+ if (*num_left == 0) {
+ // Last read was complete. Start reading a new unicode code point.
+ *cp = str[index++];
+ *num_read = 1;
+ // The length of the code point is determined from reading the first byte.
+ //
+ // If the first byte is between:
+ // 0..0x7f: that's the value of the code point.
+ // 0x80..0xbf: <invalid>
+ // 0xc0..0xdf: 11-bit code point encoded in 2 bytes.
+ // bit 10-6, bit 5-0
+ // 0xe0..0xef: 16-bit code point encoded in 3 bytes.
+ // bit 15-12, bit 11-6, bit 5-0
+ // 0xf0..0xf7: 21-bit code point encoded in 4 bytes.
+ // bit 20-18, bit 17-12, bit 11-6, bit 5-0
+ // 0xf8..0xff: <invalid>
+ //
+ // Meaning of each bit:
+ // <msb> bit 7: 0 - single byte code point: bits 6-0 are values.
+ // 1 - multibyte code point
+ // bit 6: 0 - subsequent bytes of multibyte code point:
+ // bits 5-0 are values.
+ // 1 - first byte of multibyte code point
+ // bit 5: 0 - first byte of 2-byte code point: bits 4-0 are values.
+ // 1 - first byte of code point with >= 3 bytes.
+ // bit 4: 0 - first byte of 3-byte code point: bits 3-0 are values.
+ // 1 - first byte of code point with >= 4 bytes.
+ // bit 3: 0 - first byte of 4-byte code point: bits 2-0 are values.
+ // 1 - reserved for future expansion.
+ if (*cp <= 0x7f) {
+ return true;
+ } else if (*cp <= 0xbf) {
+ return false;
+ } else if (*cp <= 0xdf) {
+ *cp &= 0x1f;
+ *num_left = 1;
+ } else if (*cp <= 0xef) {
+ *cp &= 0x0f;
+ *num_left = 2;
+ } else if (*cp <= 0xf7) {
+ *cp &= 0x07;
+ *num_left = 3;
+ } else {
+ return false;
+ }
+ } else {
+ // Last read was partial. Initialize num_read to 0 and continue reading
+ // the last unicode code point.
+ *num_read = 0;
+ }
+ while (*num_left > 0 && index < str.size()) {
+ uint32 ch = str[index++];
+ --(*num_left);
+ ++(*num_read);
+ *cp = (*cp << 6) | (ch & 0x3f);
+ if (ch < 0x80 || ch > 0xbf) return false;
+ }
+ return *num_left > 0 || (!IsSurrogate(*cp) && IsValidCodePoint(*cp));
+}
+
+// Stores the 16-bit unicode code point as its hexadecimal digits in buffer
+// and returns a StringPiece that points to this buffer. The input buffer needs
+// to be at least 6 bytes long.
+StringPiece ToHex(uint16 cp, char* buffer) {
+ buffer[5] = kHex[cp & 0x0f];
+ cp >>= 4;
+ buffer[4] = kHex[cp & 0x0f];
+ cp >>= 4;
+ buffer[3] = kHex[cp & 0x0f];
+ cp >>= 4;
+ buffer[2] = kHex[cp & 0x0f];
+ return StringPiece(buffer, 0, 6);
+}
+
+// Stores the 32-bit unicode code point as its hexadecimal digits in buffer
+// and returns a StringPiece that points to this buffer. The input buffer needs
+// to be at least 12 bytes long.
+StringPiece ToSurrogateHex(uint32 cp, char* buffer) {
+ uint16 low = ToLowSurrogate(cp);
+ uint16 high = ToHighSurrogate(cp);
+
+ buffer[11] = kHex[low & 0x0f];
+ low >>= 4;
+ buffer[10] = kHex[low & 0x0f];
+ low >>= 4;
+ buffer[9] = kHex[low & 0x0f];
+ low >>= 4;
+ buffer[8] = kHex[low & 0x0f];
+
+ buffer[5] = kHex[high & 0x0f];
+ high >>= 4;
+ buffer[4] = kHex[high & 0x0f];
+ high >>= 4;
+ buffer[3] = kHex[high & 0x0f];
+ high >>= 4;
+ buffer[2] = kHex[high & 0x0f];
+
+ return StringPiece(buffer, 12);
+}
+
+// If the given unicode code point needs escaping, then returns the
+// escaped form. The returned StringPiece either points to statically
+// pre-allocated char[] or to the given buffer. The input buffer needs
+// to be at least 12 bytes long.
+//
+// If the given unicode code point does not need escaping, an empty
+// StringPiece is returned.
+StringPiece EscapeCodePoint(uint32 cp, char* buffer) {
+ if (cp < 0xa0) return kCommonEscapes[cp];
+ switch (cp) {
+ // These are not required by json spec
+ // but used to prevent security bugs in javascript.
+ case 0xfeff: // Zero width no-break space
+ case 0xfff9: // Interlinear annotation anchor
+ case 0xfffa: // Interlinear annotation separator
+ case 0xfffb: // Interlinear annotation terminator
+
+ case 0x00ad: // Soft-hyphen
+ case 0x06dd: // Arabic end of ayah
+ case 0x070f: // Syriac abbreviation mark
+ case 0x17b4: // Khmer vowel inherent Aq
+ case 0x17b5: // Khmer vowel inherent Aa
+ return ToHex(cp, buffer);
+
+ default:
+ if ((cp >= 0x0600 && cp <= 0x0603) || // Arabic signs
+ (cp >= 0x200b && cp <= 0x200f) || // Zero width etc.
+ (cp >= 0x2028 && cp <= 0x202e) || // Separators etc.
+ (cp >= 0x2060 && cp <= 0x2064) || // Invisible etc.
+ (cp >= 0x206a && cp <= 0x206f)) { // Shaping etc.
+ return ToHex(cp, buffer);
+ }
+
+ if (cp == 0x000e0001 || // Language tag
+ (cp >= 0x0001d173 && cp <= 0x0001d17a) || // Music formatting
+ (cp >= 0x000e0020 && cp <= 0x000e007f)) { // TAG symbols
+ return ToSurrogateHex(cp, buffer);
+ }
+ }
+ return StringPiece();
+}
+
+// Tries to escape the given code point first. If the given code point
+// does not need to be escaped, but force_output is true, then render
+// the given multi-byte code point in UTF8 in the buffer and returns it.
+StringPiece EscapeCodePoint(uint32 cp, char* buffer, bool force_output) {
+ StringPiece sp = EscapeCodePoint(cp, buffer);
+ if (force_output && sp.empty()) {
+ buffer[5] = (cp & 0x3f) | 0x80;
+ cp >>= 6;
+ if (cp <= 0x1f) {
+ buffer[4] = cp | 0xc0;
+ sp.set(buffer + 4, 2);
+ return sp;
+ }
+ buffer[4] = (cp & 0x3f) | 0x80;
+ cp >>= 6;
+ if (cp <= 0x0f) {
+ buffer[3] = cp | 0xe0;
+ sp.set(buffer + 3, 3);
+ return sp;
+ }
+ buffer[3] = (cp & 0x3f) | 0x80;
+ buffer[2] = ((cp >> 6) & 0x07) | 0xf0;
+ sp.set(buffer + 2, 4);
+ }
+ return sp;
+}
+
+} // namespace
+
+void JsonEscaping::Escape(strings::ByteSource* input,
+ strings::ByteSink* output) {
+ char buffer[12] = "\\udead\\ubee";
+ uint32 cp = 0; // Current unicode code point.
+ int num_left = 0; // Num of chars to read to complete the code point.
+ while (input->Available() > 0) {
+ StringPiece str = input->Peek();
+ StringPiece escaped;
+ int i = 0;
+ int num_read;
+ bool ok;
+ bool cp_was_split = num_left > 0;
+ // Loop until we encounter either
+ // i) a code point that needs to be escaped; or
+ // ii) a split code point is completely read; or
+ // iii) a character that is not a valid utf8; or
+ // iv) end of the StringPiece str is reached.
+ do {
+ ok = ReadCodePoint(str, i, &cp, &num_left, &num_read);
+ if (num_left > 0 || !ok) break; // case iii or iv
+ escaped = EscapeCodePoint(cp, buffer, cp_was_split);
+ if (!escaped.empty()) break; // case i or ii
+ i += num_read;
+ num_read = 0;
+ } while (i < str.length()); // case iv
+ // First copy the un-escaped prefix, if any, to the output ByteSink.
+ if (i > 0) input->CopyTo(output, i);
+ if (num_read > 0) input->Skip(num_read);
+ if (!ok) {
+ // Case iii: Report error.
+ // TODO(wpoon): Add error reporting.
+ num_left = 0;
+ } else if (num_left == 0 && !escaped.empty()) {
+ // Case i or ii: Append the escaped code point to the output ByteSink.
+ output->Append(escaped.data(), escaped.size());
+ }
+ }
+ if (num_left > 0) {
+ // Treat as case iii: report error.
+ // TODO(wpoon): Add error reporting.
+ }
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/json_escaping.h b/src/google/protobuf/util/internal/json_escaping.h
new file mode 100644
index 00000000..e3e329fc
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_escaping.h
@@ -0,0 +1,91 @@
+// 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 NET_PROTO2_UTIL_CONVERTER_STRINGS_JSON_ESCAPING_H_
+#define NET_PROTO2_UTIL_CONVERTER_STRINGS_JSON_ESCAPING_H_
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/bytestream.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class JsonEscaping {
+ public:
+ // The minimum value of a unicode high-surrogate code unit in the utf-16
+ // encoding. A high-surrogate is also known as a leading-surrogate.
+ // See http://www.unicode.org/glossary/#high_surrogate_code_unit
+ static const uint16 kMinHighSurrogate = 0xd800;
+
+ // The maximum value of a unicide high-surrogate code unit in the utf-16
+ // encoding. A high-surrogate is also known as a leading-surrogate.
+ // See http://www.unicode.org/glossary/#high_surrogate_code_unit
+ static const uint16 kMaxHighSurrogate = 0xdbff;
+
+ // The minimum value of a unicode low-surrogate code unit in the utf-16
+ // encoding. A low-surrogate is also known as a trailing-surrogate.
+ // See http://www.unicode.org/glossary/#low_surrogate_code_unit
+ static const uint16 kMinLowSurrogate = 0xdc00;
+
+ // The maximum value of a unicode low-surrogate code unit in the utf-16
+ // encoding. A low-surrogate is also known as a trailing surrogate.
+ // See http://www.unicode.org/glossary/#low_surrogate_code_unit
+ static const uint16 kMaxLowSurrogate = 0xdfff;
+
+ // The minimum value of a unicode supplementary code point.
+ // See http://www.unicode.org/glossary/#supplementary_code_point
+ static const uint32 kMinSupplementaryCodePoint = 0x010000;
+
+ // The minimum value of a unicode code point.
+ // See http://www.unicode.org/glossary/#code_point
+ static const uint32 kMinCodePoint = 0x000000;
+
+ // The maximum value of a unicode code point.
+ // See http://www.unicode.org/glossary/#code_point
+ static const uint32 kMaxCodePoint = 0x10ffff;
+
+ JsonEscaping() {}
+ virtual ~JsonEscaping() {}
+
+ // Escape the given ByteSource to the given ByteSink.
+ static void Escape(strings::ByteSource* input, strings::ByteSink* output);
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JsonEscaping);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+#endif // NET_PROTO2_UTIL_CONVERTER_STRINGS_JSON_ESCAPING_H_
+} // namespace google
diff --git a/src/google/protobuf/util/internal/json_objectwriter.cc b/src/google/protobuf/util/internal/json_objectwriter.cc
new file mode 100644
index 00000000..0c41515f
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_objectwriter.cc
@@ -0,0 +1,175 @@
+// 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 <google/protobuf/util/internal/json_objectwriter.h>
+
+#include <math.h>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/util/internal/json_escaping.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using strings::ArrayByteSource;
+
+JsonObjectWriter::~JsonObjectWriter() {
+ if (!element_->is_root()) {
+ GOOGLE_LOG(WARNING) << "JsonObjectWriter was not fully closed.";
+ }
+}
+
+JsonObjectWriter* JsonObjectWriter::StartObject(StringPiece name) {
+ WritePrefix(name);
+ WriteChar('{');
+ Push();
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::EndObject() {
+ Pop();
+ WriteChar('}');
+ if (element()->is_root()) NewLine();
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::StartList(StringPiece name) {
+ WritePrefix(name);
+ WriteChar('[');
+ Push();
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::EndList() {
+ Pop();
+ WriteChar(']');
+ if (element()->is_root()) NewLine();
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderBool(StringPiece name,
+ bool value) {
+ return RenderSimple(name, value ? "true" : "false");
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderInt32(StringPiece name,
+ int32 value) {
+ return RenderSimple(name, SimpleItoa(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderUint32(StringPiece name,
+ uint32 value) {
+ return RenderSimple(name, SimpleItoa(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderInt64(StringPiece name,
+ int64 value) {
+ WritePrefix(name);
+ WriteChar('"');
+ stream_->WriteString(SimpleItoa(value));
+ WriteChar('"');
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderUint64(StringPiece name,
+ uint64 value) {
+ WritePrefix(name);
+ WriteChar('"');
+ stream_->WriteString(SimpleItoa(value));
+ WriteChar('"');
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderDouble(StringPiece name,
+ double value) {
+ if (isfinite(value)) return RenderSimple(name, SimpleDtoa(value));
+
+ // Render quoted with NaN/Infinity-aware DoubleAsString.
+ return RenderString(name, DoubleAsString(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderFloat(StringPiece name,
+ float value) {
+ if (isfinite(value)) return RenderSimple(name, SimpleFtoa(value));
+
+ // Render quoted with NaN/Infinity-aware FloatAsString.
+ return RenderString(name, FloatAsString(value));
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderString(StringPiece name,
+ StringPiece value) {
+ WritePrefix(name);
+ WriteChar('"');
+ ArrayByteSource source(value);
+ JsonEscaping::Escape(&source, &sink_);
+ WriteChar('"');
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderBytes(StringPiece name,
+ StringPiece value) {
+ WritePrefix(name);
+ string base64;
+ WebSafeBase64EscapeWithPadding(value, &base64);
+ WriteChar('"');
+ // TODO(wpoon): Consider a ByteSink solution that writes the base64 bytes
+ // directly to the stream, rather than first putting them
+ // into a string and then writing them to the stream.
+ stream_->WriteRaw(base64.data(), base64.size());
+ WriteChar('"');
+ return this;
+}
+
+JsonObjectWriter* JsonObjectWriter::RenderNull(StringPiece name) {
+ return RenderSimple(name, "null");
+}
+
+void JsonObjectWriter::WritePrefix(StringPiece name) {
+ bool not_first = !element()->is_first();
+ if (not_first) WriteChar(',');
+ if (not_first || !element()->is_root()) NewLine();
+ if (!name.empty()) {
+ WriteChar('"');
+ ArrayByteSource source(name);
+ JsonEscaping::Escape(&source, &sink_);
+ stream_->WriteString("\":");
+ if (!indent_string_.empty()) WriteChar(' ');
+ }
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/json_objectwriter.h b/src/google/protobuf/util/internal/json_objectwriter.h
new file mode 100644
index 00000000..761a0a10
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_objectwriter.h
@@ -0,0 +1,206 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_OBJECTWRITER_H__
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/util/internal/structured_objectwriter.h>
+#include <google/protobuf/stubs/bytestream.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// An ObjectWriter implementation that outputs JSON. This ObjectWriter
+// supports writing a compact form or a pretty printed form.
+//
+// Sample usage:
+// string output;
+// StringOutputStream* str_stream = new StringOutputStream(&output);
+// CodedOutputStream* out_stream = new CodedOutputStream(str_stream);
+// JsonObjectWriter* ow = new JsonObjectWriter(" ", out_stream);
+// ow->StartObject("")
+// ->RenderString("name", "value")
+// ->RenderString("emptystring", string())
+// ->StartObject("nested")
+// ->RenderInt64("light", 299792458);
+// ->RenderDouble("pi", 3.141592653589793);
+// ->EndObject()
+// ->StartList("empty")
+// ->EndList()
+// ->EndObject();
+//
+// And then the output string would become:
+// {
+// "name": "value",
+// "emptystring": "",
+// "nested": {
+// "light": "299792458",
+// "pi": 3.141592653589793
+// },
+// "empty": []
+// }
+//
+// JsonObjectWriter does not validate if calls actually result in valid JSON.
+// For example, passing an empty name when one would be required won't result
+// in an error, just an invalid output.
+//
+// Note that all int64 and uint64 are rendered as strings instead of numbers.
+// This is because JavaScript parses numbers as 64-bit float thus int64 and
+// uint64 would lose precision if rendered as numbers.
+//
+// JsonObjectWriter is thread-unsafe.
+class LIBPROTOBUF_EXPORT JsonObjectWriter : public StructuredObjectWriter {
+ public:
+ JsonObjectWriter(StringPiece indent_string,
+ google::protobuf::io::CodedOutputStream* out)
+ : element_(new Element(NULL)),
+ stream_(out), sink_(out),
+ indent_string_(indent_string.ToString()) {
+ }
+ virtual ~JsonObjectWriter();
+
+ // ObjectWriter methods.
+ virtual JsonObjectWriter* StartObject(StringPiece name);
+ virtual JsonObjectWriter* EndObject();
+ virtual JsonObjectWriter* StartList(StringPiece name);
+ virtual JsonObjectWriter* EndList();
+ virtual JsonObjectWriter* RenderBool(StringPiece name, bool value);
+ virtual JsonObjectWriter* RenderInt32(StringPiece name, int32 value);
+ virtual JsonObjectWriter* RenderUint32(StringPiece name, uint32 value);
+ virtual JsonObjectWriter* RenderInt64(StringPiece name, int64 value);
+ virtual JsonObjectWriter* RenderUint64(StringPiece name, uint64 value);
+ virtual JsonObjectWriter* RenderDouble(StringPiece name, double value);
+ virtual JsonObjectWriter* RenderFloat(StringPiece name, float value);
+ virtual JsonObjectWriter* RenderString(StringPiece name, StringPiece value);
+ virtual JsonObjectWriter* RenderBytes(StringPiece name, StringPiece value);
+ virtual JsonObjectWriter* RenderNull(StringPiece name);
+
+ protected:
+ class LIBPROTOBUF_EXPORT Element : public BaseElement {
+ public:
+ explicit Element(Element* parent) : BaseElement(parent), is_first_(true) {}
+
+ // Called before each field of the Element is to be processed.
+ // Returns true if this is the first call (processing the first field).
+ bool is_first() {
+ if (is_first_) {
+ is_first_ = false;
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ bool is_first_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(Element);
+ };
+
+ virtual Element* element() { return element_.get(); }
+
+ private:
+ class LIBPROTOBUF_EXPORT ByteSinkWrapper : public strings::ByteSink {
+ public:
+ explicit ByteSinkWrapper(google::protobuf::io::CodedOutputStream* stream)
+ : stream_(stream) {}
+ virtual ~ByteSinkWrapper() {}
+
+ // ByteSink methods.
+ virtual void Append(const char* bytes, size_t n) {
+ stream_->WriteRaw(bytes, n);
+ }
+
+ private:
+ google::protobuf::io::CodedOutputStream* stream_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSinkWrapper);
+ };
+
+ // Renders a simple value as a string. By default all non-string Render
+ // methods convert their argument to a string and call this method. This
+ // method can then be used to render the simple value without escaping it.
+ JsonObjectWriter* RenderSimple(StringPiece name, const string& value) {
+ WritePrefix(name);
+ stream_->WriteString(value);
+ return this;
+ }
+
+ // Pushes a new element to the stack.
+ void Push() { element_.reset(new Element(element_.release())); }
+
+ // Pops an element off of the stack and deletes the popped element.
+ void Pop() {
+ bool needs_newline = !element_->is_first();
+ element_.reset(element_->pop<Element>());
+ if (needs_newline) NewLine();
+ }
+
+ // If pretty printing is enabled, this will write a newline to the output,
+ // followed by optional indentation. Otherwise this method is a noop.
+ void NewLine() {
+ if (!indent_string_.empty()) {
+ WriteChar('\n');
+ for (int i = 0; i < element()->level(); i++) {
+ stream_->WriteString(indent_string_);
+ }
+ }
+ }
+
+ // Writes a prefix. This will write out any pretty printing and
+ // commas that are required, followed by the name and a ':' if
+ // the name is not null.
+ void WritePrefix(StringPiece name);
+
+ // Writes an individual character to the output.
+ void WriteChar(const char c) { stream_->WriteRaw(&c, sizeof(c)); }
+
+ google::protobuf::scoped_ptr<Element> element_;
+ google::protobuf::io::CodedOutputStream* stream_;
+ ByteSinkWrapper sink_;
+ const string indent_string_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonObjectWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/json_objectwriter_test.cc b/src/google/protobuf/util/internal/json_objectwriter_test.cc
new file mode 100644
index 00000000..df9a133e
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_objectwriter_test.cc
@@ -0,0 +1,285 @@
+// 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 <google/protobuf/util/internal/json_objectwriter.h>
+
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using google::protobuf::io::CodedOutputStream;
+using google::protobuf::io::StringOutputStream;
+
+class JsonObjectWriterTest : public ::testing::Test {
+ protected:
+ JsonObjectWriterTest()
+ : str_stream_(new StringOutputStream(&output_)),
+ out_stream_(new CodedOutputStream(str_stream_)),
+ ow_(NULL) {}
+
+ virtual ~JsonObjectWriterTest() {
+ delete ow_;
+ delete out_stream_;
+ delete str_stream_;
+ }
+
+ string output_;
+ StringOutputStream* const str_stream_;
+ CodedOutputStream* const out_stream_;
+ ObjectWriter* ow_;
+};
+
+TEST_F(JsonObjectWriterTest, EmptyRootObject) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")->EndObject();
+ EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, EmptyObject) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")
+ ->RenderString("test", "value")
+ ->StartObject("empty")
+ ->EndObject()
+ ->EndObject();
+ EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, EmptyRootList) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartList("")->EndList();
+ EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, EmptyList) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")
+ ->RenderString("test", "value")
+ ->StartList("empty")
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, ObjectInObject) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")
+ ->StartObject("nested")
+ ->RenderString("field", "value")
+ ->EndObject()
+ ->EndObject();
+ EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, ListInObject) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")
+ ->StartList("nested")
+ ->RenderString("", "value")
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ("{\"nested\":[\"value\"]}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, ObjectInList) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartList("")
+ ->StartObject("")
+ ->RenderString("field", "value")
+ ->EndObject()
+ ->EndList();
+ EXPECT_EQ("[{\"field\":\"value\"}]",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, ListInList) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartList("")
+ ->StartList("")
+ ->RenderString("", "value")
+ ->EndList()
+ ->EndList();
+ EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, RenderPrimitives) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")
+ ->RenderBool("bool", true)
+ ->RenderDouble("double", std::numeric_limits<double>::max())
+ ->RenderFloat("float", std::numeric_limits<float>::max())
+ ->RenderInt32("int", std::numeric_limits<int32>::min())
+ ->RenderInt64("long", std::numeric_limits<int64>::min())
+ ->RenderBytes("bytes", "abracadabra")
+ ->RenderString("string", "string")
+ ->RenderBytes("emptybytes", "")
+ ->RenderString("emptystring", string())
+ ->EndObject();
+ EXPECT_EQ(
+ "{\"bool\":true,"
+ "\"double\":" + ValueAsString<double>(1.7976931348623157e+308) + ","
+ "\"float\":" + ValueAsString<float>(3.4028235e+38) + ","
+ "\"int\":-2147483648,"
+ "\"long\":\"-9223372036854775808\","
+ "\"bytes\":\"YWJyYWNhZGFicmE=\","
+ "\"string\":\"string\","
+ "\"emptybytes\":\"\","
+ "\"emptystring\":\"\"}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, BytesEncodesAsWebSafeBase64) {
+ string s;
+ s.push_back('\377');
+ s.push_back('\357');
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")->RenderBytes("bytes", s)->EndObject();
+ // Non-web-safe would encode this as "/+8="
+ EXPECT_EQ("{\"bytes\":\"_-8=\"}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, PrettyPrintList) {
+ ow_ = new JsonObjectWriter(" ", out_stream_);
+ ow_->StartObject("")
+ ->StartList("items")
+ ->RenderString("", "item1")
+ ->RenderString("", "item2")
+ ->RenderString("", "item3")
+ ->EndList()
+ ->StartList("empty")
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"items\": [\n"
+ " \"item1\",\n"
+ " \"item2\",\n"
+ " \"item3\"\n"
+ " ],\n"
+ " \"empty\": []\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, PrettyPrintObject) {
+ ow_ = new JsonObjectWriter(" ", out_stream_);
+ ow_->StartObject("")
+ ->StartObject("items")
+ ->RenderString("key1", "item1")
+ ->RenderString("key2", "item2")
+ ->RenderString("key3", "item3")
+ ->EndObject()
+ ->StartObject("empty")
+ ->EndObject()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"items\": {\n"
+ " \"key1\": \"item1\",\n"
+ " \"key2\": \"item2\",\n"
+ " \"key3\": \"item3\"\n"
+ " },\n"
+ " \"empty\": {}\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) {
+ ow_ = new JsonObjectWriter(" ", out_stream_);
+ ow_->StartObject("")
+ ->StartList("list")
+ ->StartObject("")
+ ->EndObject()
+ ->EndList()
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"list\": [\n"
+ " {}\n"
+ " ]\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) {
+ ow_ = new JsonObjectWriter(" ", out_stream_);
+ ow_->StartObject("")
+ ->RenderBool("bool", true)
+ ->RenderInt32("int", 42)
+ ->EndObject();
+ EXPECT_EQ(
+ "{\n"
+ " \"bool\": true,\n"
+ " \"int\": 42\n"
+ "}\n",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")->RenderString("string", "'<>&amp;\\\"\r\n")->EndObject();
+ EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&amp;\\\\\\\"\\r\\n\"}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+TEST_F(JsonObjectWriterTest, Stringification) {
+ ow_ = new JsonObjectWriter("", out_stream_);
+ ow_->StartObject("")
+ ->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN())
+ ->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN())
+ ->RenderDouble("double_pos", std::numeric_limits<double>::infinity())
+ ->RenderFloat("float_pos", std::numeric_limits<float>::infinity())
+ ->RenderDouble("double_neg", -std::numeric_limits<double>::infinity())
+ ->RenderFloat("float_neg", -std::numeric_limits<float>::infinity())
+ ->EndObject();
+ EXPECT_EQ(
+ "{\"double_nan\":\"NaN\","
+ "\"float_nan\":\"NaN\","
+ "\"double_pos\":\"Infinity\","
+ "\"float_pos\":\"Infinity\","
+ "\"double_neg\":\"-Infinity\","
+ "\"float_neg\":\"-Infinity\"}",
+ output_.substr(0, out_stream_->ByteCount()));
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc
new file mode 100644
index 00000000..d439a221
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_stream_parser.cc
@@ -0,0 +1,740 @@
+// 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 <google/protobuf/util/internal/json_stream_parser.h>
+
+#include <algorithm>
+#include <cctype>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/object_writer.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+// Allow these symbols to be referenced as util::Status, util::error::* in
+// this file.
+using util::Status;
+namespace error {
+using util::error::INTERNAL;
+using util::error::INVALID_ARGUMENT;
+} // namespace error
+
+namespace converter {
+
+// Number of digits in a unicode escape sequence (/uXXXX)
+static const int kUnicodeEscapedLength = 6;
+
+// Length of the true, false, and null literals.
+static const int true_len = strlen("true");
+static const int false_len = strlen("false");
+static const int null_len = strlen("null");
+
+inline bool IsLetter(char c) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_') ||
+ (c == '$');
+}
+
+inline bool IsAlphanumeric(char c) {
+ return IsLetter(c) || ('0' <= c && c <= '9');
+}
+
+static bool ConsumeKey(StringPiece* input, StringPiece* key) {
+ if (input->empty() || !IsLetter((*input)[0])) return false;
+ int len = 1;
+ for (; len < input->size(); ++len) {
+ if (!IsAlphanumeric((*input)[len])) {
+ break;
+ }
+ }
+ *key = StringPiece(input->data(), len);
+ *input = StringPiece(input->data() + len, input->size() - len);
+ return true;
+}
+
+static bool MatchKey(StringPiece input) {
+ return !input.empty() && IsLetter(input[0]);
+}
+
+JsonStreamParser::JsonStreamParser(ObjectWriter* ow)
+ : ow_(ow),
+ stack_(),
+ leftover_(),
+ json_(),
+ p_(),
+ key_(),
+ key_storage_(),
+ finishing_(false),
+ parsed_(),
+ parsed_storage_(),
+ string_open_(0),
+ utf8_storage_(),
+ utf8_length_(0) {
+ // Initialize the stack with a single value to be parsed.
+ stack_.push(VALUE);
+}
+
+JsonStreamParser::~JsonStreamParser() {}
+
+util::Status JsonStreamParser::Parse(StringPiece json) {
+ return ParseChunk(json);
+}
+
+util::Status JsonStreamParser::FinishParse() {
+ // If we do not expect anything and there is nothing left to parse we're all
+ // done.
+ if (stack_.empty() && leftover_.empty()) {
+ return util::Status::OK;
+ }
+ // Parse the remainder in finishing mode, which reports errors for things like
+ // unterminated strings or unknown tokens that would normally be retried.
+ p_ = json_ = StringPiece(leftover_);
+ finishing_ = true;
+ util::Status result = RunParser();
+ if (result.ok()) {
+ SkipWhitespace();
+ if (!p_.empty()) {
+ result = ReportFailure("Parsing terminated before end of input.");
+ }
+ }
+ return result;
+}
+
+util::Status JsonStreamParser::ParseChunk(StringPiece chunk) {
+ // If we have leftovers from a previous chunk, append the new chunk to it and
+ // create a new StringPiece pointing at the string's data. This could be
+ // large but we rely on the chunks to be small, assuming they are fragments
+ // of a Cord.
+ if (!leftover_.empty()) {
+ chunk.AppendToString(&leftover_);
+ p_ = json_ = StringPiece(leftover_);
+ } else {
+ p_ = json_ = chunk;
+ }
+
+ finishing_ = false;
+ util::Status result = RunParser();
+ if (!result.ok()) return result;
+
+ SkipWhitespace();
+ if (p_.empty()) {
+ // If we parsed everything we had, clear the leftover.
+ leftover_.clear();
+ } else {
+ // If we do not expect anything i.e. stack is empty, and we have non-empty
+ // string left to parse, we report an error.
+ if (stack_.empty()) {
+ return ReportFailure("Parsing terminated before end of input.");
+ }
+ // If we expect future data i.e. stack is non-empty, and we have some
+ // unparsed data left, we save it for later parse.
+ leftover_ = p_.ToString();
+ }
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::RunParser() {
+ while (!stack_.empty()) {
+ ParseType type = stack_.top();
+ TokenType t = (string_open_ == 0) ? GetNextTokenType() : BEGIN_STRING;
+ stack_.pop();
+ util::Status result;
+ switch (type) {
+ case VALUE:
+ result = ParseValue(t);
+ break;
+
+ case OBJ_MID:
+ result = ParseObjectMid(t);
+ break;
+
+ case ENTRY:
+ result = ParseEntry(t);
+ break;
+
+ case ENTRY_MID:
+ result = ParseEntryMid(t);
+ break;
+
+ case ARRAY_VALUE:
+ result = ParseArrayValue(t);
+ break;
+
+ case ARRAY_MID:
+ result = ParseArrayMid(t);
+ break;
+
+ default:
+ result = util::Status(util::error::INTERNAL,
+ StrCat("Unknown parse type: ", type));
+ break;
+ }
+ if (!result.ok()) {
+ // If we were cancelled, save our state and try again later.
+ if (!finishing_ && result == util::Status::CANCELLED) {
+ stack_.push(type);
+ // If we have a key we still need to render, make sure to save off the
+ // contents in our own storage.
+ if (!key_.empty() && key_storage_.empty()) {
+ key_.AppendToString(&key_storage_);
+ key_ = StringPiece(key_storage_);
+ }
+ result = util::Status::OK;
+ }
+ return result;
+ }
+ }
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::ParseValue(TokenType type) {
+ switch (type) {
+ case BEGIN_OBJECT:
+ return HandleBeginObject();
+ case BEGIN_ARRAY:
+ return HandleBeginArray();
+ case BEGIN_STRING:
+ return ParseString();
+ case BEGIN_NUMBER:
+ return ParseNumber();
+ case BEGIN_TRUE:
+ return ParseTrue();
+ case BEGIN_FALSE:
+ return ParseFalse();
+ case BEGIN_NULL:
+ return ParseNull();
+ case UNKNOWN:
+ return ReportUnknown("Expected a value.");
+ default: {
+ // Special case for having been cut off while parsing, wait for more data.
+ // This handles things like 'fals' being at the end of the string, we
+ // don't know if the next char would be e, completing it, or something
+ // else, making it invalid.
+ if (!finishing_ && p_.length() < false_len) {
+ return util::Status::CANCELLED;
+ }
+ return ReportFailure("Unexpected token.");
+ }
+ }
+}
+
+util::Status JsonStreamParser::ParseString() {
+ util::Status result = ParseStringHelper();
+ if (result.ok()) {
+ ow_->RenderString(key_, parsed_);
+ key_.clear();
+ parsed_.clear();
+ parsed_storage_.clear();
+ }
+ return result;
+}
+
+util::Status JsonStreamParser::ParseStringHelper() {
+ // If we haven't seen the start quote, grab it and remember it for later.
+ if (string_open_ == 0) {
+ string_open_ = *p_.data();
+ GOOGLE_DCHECK(string_open_ == '\"' || string_open_ == '\'');
+ Advance();
+ }
+ // Track where we last copied data from so we can minimize copying.
+ const char* last = p_.data();
+ while (!p_.empty()) {
+ const char* data = p_.data();
+ if (*data == '\\') {
+ // We're about to handle an escape, copy all bytes from last to data.
+ if (last < data) {
+ parsed_storage_.append(last, data - last);
+ last = data;
+ }
+ // If we ran out of string after the \, cancel or report an error
+ // depending on if we expect more data later.
+ if (p_.length() == 1) {
+ if (!finishing_) {
+ return util::Status::CANCELLED;
+ }
+ return ReportFailure("Closing quote expected in string.");
+ }
+ // Parse a unicode escape if we found \u in the string.
+ if (data[1] == 'u') {
+ util::Status result = ParseUnicodeEscape();
+ if (!result.ok()) {
+ return result;
+ }
+ // Move last pointer past the unicode escape and continue.
+ last = p_.data();
+ continue;
+ }
+ // Handle the standard set of backslash-escaped characters.
+ switch (data[1]) {
+ case 'b':
+ parsed_storage_.push_back('\b');
+ break;
+ case 'f':
+ parsed_storage_.push_back('\f');
+ break;
+ case 'n':
+ parsed_storage_.push_back('\n');
+ break;
+ case 'r':
+ parsed_storage_.push_back('\r');
+ break;
+ case 't':
+ parsed_storage_.push_back('\t');
+ break;
+ case 'v':
+ parsed_storage_.push_back('\v');
+ break;
+ default:
+ parsed_storage_.push_back(data[1]);
+ }
+ // We handled two characters, so advance past them and continue.
+ p_.remove_prefix(2);
+ last = p_.data();
+ continue;
+ }
+ // If we found the closing quote note it, advance past it, and return.
+ if (*data == string_open_) {
+ // If we didn't copy anything, reuse the input buffer.
+ if (parsed_storage_.empty()) {
+ parsed_ = StringPiece(last, data - last);
+ } else {
+ if (last < data) {
+ parsed_storage_.append(last, data - last);
+ last = data;
+ }
+ parsed_ = StringPiece(parsed_storage_);
+ }
+ // Clear the quote char so next time we try to parse a string we'll
+ // start fresh.
+ string_open_ = 0;
+ Advance();
+ return util::Status::OK;
+ }
+ // Normal character, just advance past it.
+ Advance();
+ }
+ // If we ran out of characters, copy over what we have so far.
+ if (last < p_.data()) {
+ parsed_storage_.append(last, p_.data() - last);
+ }
+ // If we didn't find the closing quote but we expect more data, cancel for now
+ if (!finishing_) {
+ return util::Status::CANCELLED;
+ }
+ // End of string reached without a closing quote, report an error.
+ string_open_ = 0;
+ return ReportFailure("Closing quote expected in string.");
+}
+
+// Converts a unicode escaped character to a decimal value stored in a char32
+// for use in UTF8 encoding utility. We assume that str begins with \uhhhh and
+// convert that from the hex number to a decimal value.
+//
+// There are some security exploits with UTF-8 that we should be careful of:
+// - http://www.unicode.org/reports/tr36/#UTF-8_Exploit
+// - http://sites/intl-eng/design-guide/core-application
+util::Status JsonStreamParser::ParseUnicodeEscape() {
+ if (p_.length() < kUnicodeEscapedLength) {
+ if (!finishing_) {
+ return util::Status::CANCELLED;
+ }
+ return ReportFailure("Illegal hex string.");
+ }
+ GOOGLE_DCHECK_EQ('\\', p_.data()[0]);
+ GOOGLE_DCHECK_EQ('u', p_.data()[1]);
+ uint32 code = 0;
+ for (int i = 2; i < kUnicodeEscapedLength; ++i) {
+ if (!isxdigit(p_.data()[i])) {
+ return ReportFailure("Invalid escape sequence.");
+ }
+ code = (code << 4) + hex_digit_to_int(p_.data()[i]);
+ }
+ char buf[UTFmax];
+ int len = EncodeAsUTF8Char(code, buf);
+ // Advance past the unicode escape.
+ p_.remove_prefix(kUnicodeEscapedLength);
+ parsed_storage_.append(buf, len);
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::ParseNumber() {
+ NumberResult number;
+ util::Status result = ParseNumberHelper(&number);
+ if (result.ok()) {
+ switch (number.type) {
+ case NumberResult::DOUBLE:
+ ow_->RenderDouble(key_, number.double_val);
+ key_.clear();
+ break;
+
+ case NumberResult::INT:
+ ow_->RenderInt64(key_, number.int_val);
+ key_.clear();
+ break;
+
+ case NumberResult::UINT:
+ ow_->RenderUint64(key_, number.uint_val);
+ key_.clear();
+ break;
+
+ default:
+ return ReportFailure("Unable to parse number.");
+ }
+ }
+ return result;
+}
+
+util::Status JsonStreamParser::ParseNumberHelper(NumberResult* result) {
+ const char* data = p_.data();
+ int length = p_.length();
+
+ // Look for the first non-numeric character, or the end of the string.
+ int index = 0;
+ bool floating = false;
+ bool negative = data[index] == '-';
+ // Find the first character that cannot be part of the number. Along the way
+ // detect if the number needs to be parsed as a double.
+ // Note that this restricts numbers to the JSON specification, so for example
+ // we do not support hex or octal notations.
+ for (; index < length; ++index) {
+ char c = data[index];
+ if (isdigit(c)) continue;
+ if (c == '.' || c == 'e' || c == 'E') {
+ floating = true;
+ continue;
+ }
+ if (c == '+' || c == '-') continue;
+ // Not a valid number character, break out.
+ break;
+ }
+
+ // If the entire input is a valid number, and we may have more content in the
+ // future, we abort for now and resume when we know more.
+ if (index == length && !finishing_) {
+ return util::Status::CANCELLED;
+ }
+
+ // Create a string containing just the number, so we can use safe_strtoX
+ string number = p_.substr(0, index).ToString();
+
+ // Floating point number, parse as a double.
+ if (floating) {
+ if (!safe_strtod(number, &result->double_val)) {
+ return ReportFailure("Unable to parse number.");
+ }
+ result->type = NumberResult::DOUBLE;
+ p_.remove_prefix(index);
+ return util::Status::OK;
+ }
+
+ // Positive non-floating point number, parse as a uint64.
+ if (!negative) {
+ if (!safe_strtou64(number, &result->uint_val)) {
+ return ReportFailure("Unable to parse number.");
+ }
+ result->type = NumberResult::UINT;
+ p_.remove_prefix(index);
+ return util::Status::OK;
+ }
+
+ // Negative non-floating point number, parse as an int64.
+ if (!safe_strto64(number, &result->int_val)) {
+ return ReportFailure("Unable to parse number.");
+ }
+ result->type = NumberResult::INT;
+ p_.remove_prefix(index);
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::HandleBeginObject() {
+ GOOGLE_DCHECK_EQ('{', *p_.data());
+ Advance();
+ ow_->StartObject(key_);
+ key_.clear();
+ stack_.push(ENTRY);
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::ParseObjectMid(TokenType type) {
+ if (type == UNKNOWN) {
+ return ReportUnknown("Expected , or } after key:value pair.");
+ }
+
+ // Object is complete, advance past the comma and render the EndObject.
+ if (type == END_OBJECT) {
+ Advance();
+ ow_->EndObject();
+ return util::Status::OK;
+ }
+ // Found a comma, advance past it and get ready for an entry.
+ if (type == VALUE_SEPARATOR) {
+ Advance();
+ stack_.push(ENTRY);
+ return util::Status::OK;
+ }
+ // Illegal token after key:value pair.
+ return ReportFailure("Expected , or } after key:value pair.");
+}
+
+util::Status JsonStreamParser::ParseEntry(TokenType type) {
+ if (type == UNKNOWN) {
+ return ReportUnknown("Expected an object key or }.");
+ }
+
+ // Close the object and return. This allows for trailing commas.
+ if (type == END_OBJECT) {
+ ow_->EndObject();
+ Advance();
+ return util::Status::OK;
+ }
+
+ util::Status result;
+ if (type == BEGIN_STRING) {
+ // Key is a string (standard JSON), parse it and store the string.
+ result = ParseStringHelper();
+ if (result.ok()) {
+ key_storage_.clear();
+ if (!parsed_storage_.empty()) {
+ parsed_storage_.swap(key_storage_);
+ key_ = StringPiece(key_storage_);
+ } else {
+ key_ = parsed_;
+ }
+ parsed_.clear();
+ }
+ } else if (type == BEGIN_KEY) {
+ // Key is a bare key (back compat), create a StringPiece pointing to it.
+ result = ParseKey();
+ } else {
+ // Unknown key type, report an error.
+ result = ReportFailure("Expected an object key or }.");
+ }
+ // On success we next expect an entry mid ':' then an object mid ',' or '}'
+ if (result.ok()) {
+ stack_.push(OBJ_MID);
+ stack_.push(ENTRY_MID);
+ }
+ return result;
+}
+
+util::Status JsonStreamParser::ParseEntryMid(TokenType type) {
+ if (type == UNKNOWN) {
+ return ReportUnknown("Expected : between key:value pair.");
+ }
+ if (type == ENTRY_SEPARATOR) {
+ Advance();
+ stack_.push(VALUE);
+ return util::Status::OK;
+ }
+ return ReportFailure("Expected : between key:value pair.");
+}
+
+util::Status JsonStreamParser::HandleBeginArray() {
+ GOOGLE_DCHECK_EQ('[', *p_.data());
+ Advance();
+ ow_->StartList(key_);
+ key_.clear();
+ stack_.push(ARRAY_VALUE);
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::ParseArrayValue(TokenType type) {
+ if (type == UNKNOWN) {
+ return ReportUnknown("Expected a value or ] within an array.");
+ }
+
+ if (type == END_ARRAY) {
+ ow_->EndList();
+ Advance();
+ return util::Status::OK;
+ }
+
+ // The ParseValue call may push something onto the stack so we need to make
+ // sure an ARRAY_MID is after it, so we push it on now.
+ stack_.push(ARRAY_MID);
+ util::Status result = ParseValue(type);
+ if (result == util::Status::CANCELLED) {
+ // If we were cancelled, pop back off the ARRAY_MID so we don't try to
+ // push it on again when we try over.
+ stack_.pop();
+ }
+ return result;
+}
+
+util::Status JsonStreamParser::ParseArrayMid(TokenType type) {
+ if (type == UNKNOWN) {
+ return ReportUnknown("Expected , or ] after array value.");
+ }
+
+ if (type == END_ARRAY) {
+ ow_->EndList();
+ Advance();
+ return util::Status::OK;
+ }
+
+ // Found a comma, advance past it and expect an array value next.
+ if (type == VALUE_SEPARATOR) {
+ Advance();
+ stack_.push(ARRAY_VALUE);
+ return util::Status::OK;
+ }
+ // Illegal token after array value.
+ return ReportFailure("Expected , or ] after array value.");
+}
+
+util::Status JsonStreamParser::ParseTrue() {
+ ow_->RenderBool(key_, true);
+ key_.clear();
+ p_.remove_prefix(true_len);
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::ParseFalse() {
+ ow_->RenderBool(key_, false);
+ key_.clear();
+ p_.remove_prefix(false_len);
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::ParseNull() {
+ ow_->RenderNull(key_);
+ key_.clear();
+ p_.remove_prefix(null_len);
+ return util::Status::OK;
+}
+
+util::Status JsonStreamParser::ReportFailure(StringPiece message) {
+ static const int kContextLength = 20;
+ const char* p_start = p_.data();
+ const char* json_start = json_.data();
+ const char* begin = max(p_start - kContextLength, json_start);
+ const char* end = min(p_start + kContextLength, json_start + json_.size());
+ StringPiece segment(begin, end - begin);
+ string location(p_start - begin, ' ');
+ location.push_back('^');
+ return util::Status(util::error::INVALID_ARGUMENT,
+ StrCat(message, "\n", segment, "\n", location));
+}
+
+util::Status JsonStreamParser::ReportUnknown(StringPiece message) {
+ // If we aren't finishing the parse, cancel parsing and try later.
+ if (!finishing_) {
+ return util::Status::CANCELLED;
+ }
+ if (p_.empty()) {
+ return ReportFailure(StrCat("Unexpected end of string. ", message));
+ }
+ return ReportFailure(message);
+}
+
+void JsonStreamParser::SkipWhitespace() {
+ while (!p_.empty() && ascii_isspace(*p_.data())) {
+ Advance();
+ }
+}
+
+void JsonStreamParser::Advance() {
+ // Advance by moving one UTF8 character while making sure we don't go beyond
+ // the length of StringPiece.
+ p_.remove_prefix(
+ min<int>(p_.length(), UTF8FirstLetterNumBytes(p_.data(), p_.length())));
+}
+
+util::Status JsonStreamParser::ParseKey() {
+ StringPiece original = p_;
+ if (!ConsumeKey(&p_, &key_)) {
+ return ReportFailure("Invalid key or variable name.");
+ }
+ // If we consumed everything but expect more data, reset p_ and cancel since
+ // we can't know if the key was complete or not.
+ if (!finishing_ && p_.empty()) {
+ p_ = original;
+ return util::Status::CANCELLED;
+ }
+ // Since we aren't using the key storage, clear it out.
+ key_storage_.clear();
+ return util::Status::OK;
+}
+
+JsonStreamParser::TokenType JsonStreamParser::GetNextTokenType() {
+ SkipWhitespace();
+
+ int size = p_.size();
+ if (size == 0) {
+ // If we ran out of data, report unknown and we'll place the previous parse
+ // type onto the stack and try again when we have more data.
+ return UNKNOWN;
+ }
+ // TODO(sven): Split this method based on context since different contexts
+ // support different tokens. Would slightly speed up processing?
+ const char* data = p_.data();
+ if (*data == '\"' || *data == '\'') return BEGIN_STRING;
+ if (*data == '-' || ('0' <= *data && *data <= '9')) {
+ return BEGIN_NUMBER;
+ }
+ if (size >= true_len && !strncmp(data, "true", true_len)) {
+ return BEGIN_TRUE;
+ }
+ if (size >= false_len && !strncmp(data, "false", false_len)) {
+ return BEGIN_FALSE;
+ }
+ if (size >= null_len && !strncmp(data, "null", null_len)) {
+ return BEGIN_NULL;
+ }
+ if (*data == '{') return BEGIN_OBJECT;
+ if (*data == '}') return END_OBJECT;
+ if (*data == '[') return BEGIN_ARRAY;
+ if (*data == ']') return END_ARRAY;
+ if (*data == ':') return ENTRY_SEPARATOR;
+ if (*data == ',') return VALUE_SEPARATOR;
+ if (MatchKey(p_)) {
+ return BEGIN_KEY;
+ }
+
+ // We don't know that we necessarily have an invalid token here, just that we
+ // can't parse what we have so far. So we don't report an error and just
+ // return UNKNOWN so we can try again later when we have more data, or if we
+ // finish and we have leftovers.
+ return UNKNOWN;
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/json_stream_parser.h b/src/google/protobuf/util/internal/json_stream_parser.h
new file mode 100644
index 00000000..17b094ae
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_stream_parser.h
@@ -0,0 +1,256 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_STREAM_PARSER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_STREAM_PARSER_H__
+
+#include <stack>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace util {
+class Status;
+} // namespace util
+
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class ObjectWriter;
+
+// A JSON parser that can parse a stream of JSON chunks rather than needing the
+// entire JSON string up front. It is a modified version of the parser in
+// //net/proto/json/json-parser.h that has been changed in the following ways:
+// - Changed from recursion to an explicit stack to allow resumption
+// - Added support for int64 and uint64 numbers
+// - Removed support for octal and decimal escapes
+// - Removed support for numeric keys
+// - Removed support for functions (javascript)
+// - Removed some lax-comma support (but kept trailing comma support)
+// - Writes directly to an ObjectWriter rather than using subclassing
+//
+// Here is an example usage:
+// JsonStreamParser parser(ow_.get());
+// util::Status result = parser.Parse(chunk1);
+// result.Update(parser.Parse(chunk2));
+// result.Update(parser.FinishParse());
+// GOOGLE_DCHECK(result.ok()) << "Failed to parse JSON";
+//
+// This parser is thread-compatible as long as only one thread is calling a
+// Parse() method at a time.
+class LIBPROTOBUF_EXPORT JsonStreamParser {
+ public:
+ // Creates a JsonStreamParser that will write to the given ObjectWriter.
+ explicit JsonStreamParser(ObjectWriter* ow);
+ virtual ~JsonStreamParser();
+
+ // Parse a JSON string (UTF-8 encoded).
+ util::Status Parse(StringPiece json);
+
+ // Finish parsing the JSON string.
+ util::Status FinishParse();
+
+ private:
+ enum TokenType {
+ BEGIN_STRING, // " or '
+ BEGIN_NUMBER, // - or digit
+ BEGIN_TRUE, // true
+ BEGIN_FALSE, // false
+ BEGIN_NULL, // null
+ BEGIN_OBJECT, // {
+ END_OBJECT, // }
+ BEGIN_ARRAY, // [
+ END_ARRAY, // ]
+ ENTRY_SEPARATOR, // :
+ VALUE_SEPARATOR, // ,
+ BEGIN_KEY, // letter, _, $ or digit. Must begin with non-digit
+ UNKNOWN // Unknown token or we ran out of the stream.
+ };
+
+ enum ParseType {
+ VALUE, // Expects a {, [, true, false, null, string or number
+ OBJ_MID, // Expects a ',' or }
+ ENTRY, // Expects a key or }
+ ENTRY_MID, // Expects a :
+ ARRAY_VALUE, // Expects a value or ]
+ ARRAY_MID // Expects a ',' or ]
+ };
+
+ // Holds the result of parsing a number
+ struct NumberResult {
+ enum Type { DOUBLE, INT, UINT };
+ Type type;
+ union {
+ double double_val;
+ int64 int_val;
+ uint64 uint_val;
+ };
+ };
+
+ // Parses a single chunk of JSON, returning an error if the JSON was invalid.
+ util::Status ParseChunk(StringPiece json);
+
+ // Runs the parser based on stack_ and p_, until the stack is empty or p_ runs
+ // out of data. If we unexpectedly run out of p_ we push the latest back onto
+ // the stack and return.
+ util::Status RunParser();
+
+ // Parses a value from p_ and writes it to ow_.
+ // A value may be an object, array, true, false, null, string or number.
+ util::Status ParseValue(TokenType type);
+
+ // Parses a string and writes it out to the ow_.
+ util::Status ParseString();
+
+ // Parses a string, storing the result in parsed_.
+ util::Status ParseStringHelper();
+
+ // This function parses unicode escape sequences in strings. It returns an
+ // error when there's a parsing error, either the size is not the expected
+ // size or a character is not a hex digit. When it returns str will contain
+ // what has been successfully parsed so far.
+ util::Status ParseUnicodeEscape();
+
+ // Expects p_ to point to a JSON number, writes the number to the writer using
+ // the appropriate Render method based on the type of number.
+ util::Status ParseNumber();
+
+ // Parse a number into a NumberResult, reporting an error if no number could
+ // be parsed. This method will try to parse into a uint64, int64, or double
+ // based on whether the number was positive or negative or had a decimal
+ // component.
+ util::Status ParseNumberHelper(NumberResult* result);
+
+ // Handles a { during parsing of a value.
+ util::Status HandleBeginObject();
+
+ // Parses from the ENTRY state.
+ util::Status ParseEntry(TokenType type);
+
+ // Parses from the ENTRY_MID state.
+ util::Status ParseEntryMid(TokenType type);
+
+ // Parses from the OBJ_MID state.
+ util::Status ParseObjectMid(TokenType type);
+
+ // Handles a [ during parsing of a value.
+ util::Status HandleBeginArray();
+
+ // Parses from the ARRAY_VALUE state.
+ util::Status ParseArrayValue(TokenType type);
+
+ // Parses from the ARRAY_MID state.
+ util::Status ParseArrayMid(TokenType type);
+
+ // Expects p_ to point to an unquoted literal
+ util::Status ParseTrue();
+ util::Status ParseFalse();
+ util::Status ParseNull();
+
+ // Report a failure as a util::Status.
+ util::Status ReportFailure(StringPiece message);
+
+ // Report a failure due to an UNKNOWN token type. We check if we hit the
+ // end of the stream and if we're finishing or not to detect what type of
+ // status to return in this case.
+ util::Status ReportUnknown(StringPiece message);
+
+ // Advance p_ past all whitespace or until the end of the string.
+ void SkipWhitespace();
+
+ // Advance p_ one UTF-8 character
+ void Advance();
+
+ // Expects p_ to point to the beginning of a key.
+ util::Status ParseKey();
+
+ // Return the type of the next token at p_.
+ TokenType GetNextTokenType();
+
+ // The object writer to write parse events to.
+ ObjectWriter* ow_;
+
+ // The stack of parsing we still need to do. When the stack runs empty we will
+ // have parsed a single value from the root (e.g. an object or list).
+ std::stack<ParseType> stack_;
+
+ // Contains any leftover text from a previous chunk that we weren't able to
+ // fully parse, for example the start of a key or number.
+ string leftover_;
+
+ // The current chunk of JSON being parsed. Primarily used for providing
+ // context during error reporting.
+ StringPiece json_;
+
+ // A pointer within the current JSON being parsed, used to track location.
+ StringPiece p_;
+
+ // Stores the last key read, as we separate parsing of keys and values.
+ StringPiece key_;
+
+ // Storage for key_ if we need to keep ownership, for example between chunks
+ // or if the key was unescaped from a JSON string.
+ string key_storage_;
+
+ // True during the FinishParse() call, so we know that any errors are fatal.
+ // For example an unterminated string will normally result in cancelling and
+ // trying during the next chunk, but during FinishParse() it is an error.
+ bool finishing_;
+
+ // String we parsed during a call to ParseStringHelper().
+ StringPiece parsed_;
+
+ // Storage for the string we parsed. This may be empty if the string was able
+ // to be parsed directly from the input.
+ string parsed_storage_;
+
+ // The character that opened the string, either ' or ".
+ // A value of 0 indicates that string parsing is not in process.
+ char string_open_;
+
+ // Storage for utf8-coerced bytes.
+ google::protobuf::scoped_array<char> utf8_storage_;
+
+ // Length of the storage for utf8-coerced bytes.
+ int utf8_length_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonStreamParser);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_STREAM_PARSER_H__
diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc
new file mode 100644
index 00000000..b0775a2f
--- /dev/null
+++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc
@@ -0,0 +1,702 @@
+// 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 <google/protobuf/util/internal/json_stream_parser.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/util/internal/expecting_objectwriter.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <gtest/gtest.h>
+#include <google/protobuf/stubs/status.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+using util::Status;
+namespace error {
+using util::error::INVALID_ARGUMENT;
+} // namespace error
+namespace converter {
+
+using util::Status;
+
+// Tests for the JSON Stream Parser. These tests are intended to be
+// comprehensive and cover the following:
+//
+// Positive tests:
+// - true, false, null
+// - empty object or array.
+// - negative and positive double and int, unsigned int
+// - single and double quoted strings
+// - string key, unquoted key, numeric key
+// - array containing array, object, value
+// - object containing array, object, value
+// - unicode handling in strings
+// - ascii escaping (\b, \f, \n, \r, \t, \v)
+// - trailing commas
+//
+// Negative tests:
+// - illegal literals
+// - mismatched quotes failure on strings
+// - unterminated string failure
+// - unexpected end of string failure
+// - mismatched object and array closing
+// - Failure to close array or object
+// - numbers too large
+// - invalid unicode escapes.
+// - invalid unicode sequences.
+// - numbers as keys
+//
+// For each test we split the input string on every possible character to ensure
+// the parser is able to handle arbitrarily split input for all cases. We also
+// do a final test of the entire test case one character at a time.
+class JsonStreamParserTest : public ::testing::Test {
+ protected:
+ JsonStreamParserTest() : mock_(), ow_(&mock_) {}
+ virtual ~JsonStreamParserTest() {}
+
+ util::Status RunTest(StringPiece json, int split) {
+ JsonStreamParser parser(&mock_);
+
+ // Special case for split == length, test parsing one character at a time.
+ if (split == json.length()) {
+ GOOGLE_LOG(INFO) << "Testing split every char: " << json;
+ for (int i = 0; i < json.length(); ++i) {
+ StringPiece single = json.substr(i, 1);
+ util::Status result = parser.Parse(single);
+ if (!result.ok()) {
+ return result;
+ }
+ }
+ return parser.FinishParse();
+ }
+
+ // Normal case, split at the split point and parse two substrings.
+ StringPiece first = json.substr(0, split);
+ StringPiece rest = json.substr(split);
+ GOOGLE_LOG(INFO) << "Testing split: " << first << "><" << rest;
+ util::Status result = parser.Parse(first);
+ if (result.ok()) {
+ result = parser.Parse(rest);
+ if (result.ok()) {
+ result = parser.FinishParse();
+ }
+ }
+ return result;
+ }
+
+ void DoTest(StringPiece json, int split) {
+ util::Status result = RunTest(json, split);
+ if (!result.ok()) {
+ GOOGLE_LOG(WARNING) << result;
+ }
+ EXPECT_OK(result);
+ }
+
+ void DoErrorTest(StringPiece json, int split, StringPiece error_prefix) {
+ util::Status result = RunTest(json, split);
+ EXPECT_EQ(util::error::INVALID_ARGUMENT, result.error_code());
+ StringPiece error_message(result.error_message());
+ EXPECT_EQ(error_prefix, error_message.substr(0, error_prefix.size()));
+ }
+
+
+ MockObjectWriter mock_;
+ ExpectingObjectWriter ow_;
+};
+
+
+// Positive tests
+
+// - true, false, null
+TEST_F(JsonStreamParserTest, SimpleTrue) {
+ StringPiece str = "true";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderBool("", true);
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleFalse) {
+ StringPiece str = "false";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderBool("", false);
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleNull) {
+ StringPiece str = "null";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderNull("");
+ DoTest(str, i);
+ }
+}
+
+// - empty object and array.
+TEST_F(JsonStreamParserTest, EmptyObject) {
+ StringPiece str = "{}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")->EndObject();
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, EmptyList) {
+ StringPiece str = "[]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("")->EndList();
+ DoTest(str, i);
+ }
+}
+
+// - negative and positive double and int, unsigned int
+TEST_F(JsonStreamParserTest, SimpleDouble) {
+ StringPiece str = "42.5";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderDouble("", 42.5);
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, ScientificDouble) {
+ StringPiece str = "1.2345e-10";
+ for (int i = 0; i < str.length(); ++i) {
+ ow_.RenderDouble("", 1.2345e-10);
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleNegativeDouble) {
+ StringPiece str = "-1045.235";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderDouble("", -1045.235);
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleInt) {
+ StringPiece str = "123456";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderUint64("", 123456);
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleNegativeInt) {
+ StringPiece str = "-79497823553162765";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderInt64("", -79497823553162765LL);
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleUnsignedInt) {
+ StringPiece str = "11779497823553162765";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderUint64("", 11779497823553162765ULL);
+ DoTest(str, i);
+ }
+}
+
+// - single and double quoted strings
+TEST_F(JsonStreamParserTest, EmptyDoubleQuotedString) {
+ StringPiece str = "\"\"";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderString("", "");
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, EmptySingleQuotedString) {
+ StringPiece str = "''";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderString("", "");
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleDoubleQuotedString) {
+ StringPiece str = "\"Some String\"";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderString("", "Some String");
+ DoTest(str, i);
+ }
+}
+
+TEST_F(JsonStreamParserTest, SimpleSingleQuotedString) {
+ StringPiece str = "'Another String'";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderString("", "Another String");
+ DoTest(str, i);
+ }
+}
+
+// - string key, unquoted key, numeric key
+TEST_F(JsonStreamParserTest, ObjectKeyTypes) {
+ StringPiece str =
+ "{'s': true, \"d\": false, key: null, snake_key: [], camelKey: {}}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")
+ ->RenderBool("s", true)
+ ->RenderBool("d", false)
+ ->RenderNull("key")
+ ->StartList("snake_key")
+ ->EndList()
+ ->StartObject("camelKey")
+ ->EndObject()
+ ->EndObject();
+ DoTest(str, i);
+ }
+}
+
+// - array containing array, object, values (true, false, null, num, string)
+TEST_F(JsonStreamParserTest, ArrayValues) {
+ StringPiece str =
+ "[true, false, null, 'a string', \"another string\", [22, -127, 45.3, "
+ "-1056.4, 11779497823553162765], {'key': true}]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("")
+ ->RenderBool("", true)
+ ->RenderBool("", false)
+ ->RenderNull("")
+ ->RenderString("", "a string")
+ ->RenderString("", "another string")
+ ->StartList("")
+ ->RenderUint64("", 22)
+ ->RenderInt64("", -127)
+ ->RenderDouble("", 45.3)
+ ->RenderDouble("", -1056.4)
+ ->RenderUint64("", 11779497823553162765ULL)
+ ->EndList()
+ ->StartObject("")
+ ->RenderBool("key", true)
+ ->EndObject()
+ ->EndList();
+ DoTest(str, i);
+ }
+}
+
+// - object containing array, object, value (true, false, null, num, string)
+TEST_F(JsonStreamParserTest, ObjectValues) {
+ StringPiece str =
+ "{t: true, f: false, n: null, s: 'a string', d: \"another string\", pi: "
+ "22, ni: -127, pd: 45.3, nd: -1056.4, pl: 11779497823553162765, l: [[]], "
+ "o: {'key': true}}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")
+ ->RenderBool("t", true)
+ ->RenderBool("f", false)
+ ->RenderNull("n")
+ ->RenderString("s", "a string")
+ ->RenderString("d", "another string")
+ ->RenderUint64("pi", 22)
+ ->RenderInt64("ni", -127)
+ ->RenderDouble("pd", 45.3)
+ ->RenderDouble("nd", -1056.4)
+ ->RenderUint64("pl", 11779497823553162765ULL)
+ ->StartList("l")
+ ->StartList("")
+ ->EndList()
+ ->EndList()
+ ->StartObject("o")
+ ->RenderBool("key", true)
+ ->EndObject()
+ ->EndObject();
+ DoTest(str, i);
+ }
+}
+
+#ifndef _MSC_VER
+// - unicode handling in strings
+TEST_F(JsonStreamParserTest, UnicodeEscaping) {
+ StringPiece str = "[\"\\u0639\\u0631\\u0628\\u0649\"]";
+ for (int i = 0; i <= str.length(); ++i) {
+ // TODO(xiaofeng): Figure out what default encoding to use for JSON strings.
+ // In protobuf we use UTF-8 for strings, but for JSON we probably should allow
+ // different encodings?
+ ow_.StartList("")->RenderString("", "\u0639\u0631\u0628\u0649")->EndList();
+ DoTest(str, i);
+ }
+}
+#endif
+
+// - ascii escaping (\b, \f, \n, \r, \t, \v)
+TEST_F(JsonStreamParserTest, AsciiEscaping) {
+ StringPiece str =
+ "[\"\\b\", \"\\ning\", \"test\\f\", \"\\r\\t\", \"test\\\\\\ving\"]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("")
+ ->RenderString("", "\b")
+ ->RenderString("", "\ning")
+ ->RenderString("", "test\f")
+ ->RenderString("", "\r\t")
+ ->RenderString("", "test\\\ving")
+ ->EndList();
+ DoTest(str, i);
+ }
+}
+
+// - trailing commas, we support a single trailing comma but no internal commas.
+TEST_F(JsonStreamParserTest, TrailingCommas) {
+ StringPiece str = "[['a',true,], {b: null,},]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("")
+ ->StartList("")
+ ->RenderString("", "a")
+ ->RenderBool("", true)
+ ->EndList()
+ ->StartObject("")
+ ->RenderNull("b")
+ ->EndObject()
+ ->EndList();
+ DoTest(str, i);
+ }
+}
+
+// Negative tests
+
+// illegal literals
+TEST_F(JsonStreamParserTest, ExtraTextAfterTrue) {
+ StringPiece str = "truee";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderBool("", true);
+ DoErrorTest(str, i, "Parsing terminated before end of input.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidNumberDashOnly) {
+ StringPiece str = "-";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Unable to parse number.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidNumberDashName) {
+ StringPiece str = "-foo";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Unable to parse number.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidLiteralInArray) {
+ StringPiece str = "[nule]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("");
+ DoErrorTest(str, i, "Unexpected token.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidLiteralInObject) {
+ StringPiece str = "{123false}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected an object key or }.");
+ }
+}
+
+// mismatched quotes failure on strings
+TEST_F(JsonStreamParserTest, MismatchedSingleQuotedLiteral) {
+ StringPiece str = "'Some str\"";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Closing quote expected in string.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, MismatchedDoubleQuotedLiteral) {
+ StringPiece str = "\"Another string that ends poorly!'";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Closing quote expected in string.");
+ }
+}
+
+// unterminated strings
+TEST_F(JsonStreamParserTest, UnterminatedLiteralString) {
+ StringPiece str = "\"Forgot the rest of i";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Closing quote expected in string.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, UnterminatedStringEscape) {
+ StringPiece str = "\"Forgot the rest of \\";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Closing quote expected in string.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, UnterminatedStringInArray) {
+ StringPiece str = "[\"Forgot to close the string]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("");
+ DoErrorTest(str, i, "Closing quote expected in string.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, UnterminatedStringInObject) {
+ StringPiece str = "{f: \"Forgot to close the string}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Closing quote expected in string.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, UnterminatedObject) {
+ StringPiece str = "{";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Unexpected end of string.");
+ }
+}
+
+
+// mismatched object and array closing
+TEST_F(JsonStreamParserTest, MismatchedCloseObject) {
+ StringPiece str = "{'key': true]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")->RenderBool("key", true);
+ DoErrorTest(str, i, "Expected , or } after key:value pair.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, MismatchedCloseArray) {
+ StringPiece str = "[true, null}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("")->RenderBool("", true)->RenderNull("");
+ DoErrorTest(str, i, "Expected , or ] after array value.");
+ }
+}
+
+// Invalid object keys.
+TEST_F(JsonStreamParserTest, InvalidNumericObjectKey) {
+ StringPiece str = "{42: true}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected an object key or }.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidLiteralObjectInObject) {
+ StringPiece str = "{{bob: true}}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected an object key or }.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidLiteralArrayInObject) {
+ StringPiece str = "{[null]}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected an object key or }.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidLiteralValueInObject) {
+ StringPiece str = "{false}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected an object key or }.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, MissingColonAfterStringInObject) {
+ StringPiece str = "{\"key\"}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected : between key:value pair.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, MissingColonAfterKeyInObject) {
+ StringPiece str = "{key}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected : between key:value pair.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, EndOfTextAfterKeyInObject) {
+ StringPiece str = "{key";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Unexpected end of string.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, MissingValueAfterColonInObject) {
+ StringPiece str = "{key:}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Unexpected token.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, MissingCommaBetweenObjectEntries) {
+ StringPiece str = "{key:20 'hello': true}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")->RenderUint64("key", 20);
+ DoErrorTest(str, i, "Expected , or } after key:value pair.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, InvalidLiteralAsObjectKey) {
+ StringPiece str = "{false: 20}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected an object key or }.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, ExtraCharactersAfterObject) {
+ StringPiece str = "{}}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")->EndObject();
+ DoErrorTest(str, i, "Parsing terminated before end of input.");
+ }
+}
+
+// numbers too large
+TEST_F(JsonStreamParserTest, PositiveNumberTooBig) {
+ StringPiece str = "[18446744073709551616]"; // 2^64
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("");
+ DoErrorTest(str, i, "Unable to parse number.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, NegativeNumberTooBig) {
+ StringPiece str = "[-18446744073709551616]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("");
+ DoErrorTest(str, i, "Unable to parse number.");
+ }
+}
+
+/*
+TODO(sven): Fail parsing when parsing a double that is too large.
+
+TEST_F(JsonStreamParserTest, DoubleTooBig) {
+ StringPiece str = "[184464073709551232321616.45]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("");
+ DoErrorTest(str, i, "Unable to parse number");
+ }
+}
+*/
+
+// invalid unicode sequence.
+TEST_F(JsonStreamParserTest, UnicodeEscapeCutOff) {
+ StringPiece str = "\"\\u12";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Illegal hex string.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, UnicodeEscapeInvalidCharacters) {
+ StringPiece str = "\"\\u12$4hello";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Invalid escape sequence.");
+ }
+}
+
+// Extra commas with an object or array.
+TEST_F(JsonStreamParserTest, ExtraCommaInObject) {
+ StringPiece str = "{'k1': true,,'k2': false}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")->RenderBool("k1", true);
+ DoErrorTest(str, i, "Expected an object key or }.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, ExtraCommaInArray) {
+ StringPiece str = "[true,,false}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("")->RenderBool("", true);
+ DoErrorTest(str, i, "Unexpected token.");
+ }
+}
+
+// Extra text beyond end of value.
+TEST_F(JsonStreamParserTest, ExtraTextAfterLiteral) {
+ StringPiece str = "'hello', 'world'";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.RenderString("", "hello");
+ DoErrorTest(str, i, "Parsing terminated before end of input.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, ExtraTextAfterObject) {
+ StringPiece str = "{'key': true} 'oops'";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("")->RenderBool("key", true)->EndObject();
+ DoErrorTest(str, i, "Parsing terminated before end of input.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, ExtraTextAfterArray) {
+ StringPiece str = "[null] 'oops'";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("")->RenderNull("")->EndList();
+ DoErrorTest(str, i, "Parsing terminated before end of input.");
+ }
+}
+
+// Random unknown text in the value.
+TEST_F(JsonStreamParserTest, UnknownCharactersAsValue) {
+ StringPiece str = "*&#25";
+ for (int i = 0; i <= str.length(); ++i) {
+ DoErrorTest(str, i, "Expected a value.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, UnknownCharactersInArray) {
+ StringPiece str = "[*&#25]";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartList("");
+ DoErrorTest(str, i, "Expected a value or ] within an array.");
+ }
+}
+
+TEST_F(JsonStreamParserTest, UnknownCharactersInObject) {
+ StringPiece str = "{'key': *&#25}";
+ for (int i = 0; i <= str.length(); ++i) {
+ ow_.StartObject("");
+ DoErrorTest(str, i, "Expected a value.");
+ }
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/location_tracker.h b/src/google/protobuf/util/internal/location_tracker.h
new file mode 100644
index 00000000..0864b057
--- /dev/null
+++ b/src/google/protobuf/util/internal/location_tracker.h
@@ -0,0 +1,65 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// LocationTrackerInterface is an interface for classes that track
+// the location information for the purpose of error reporting.
+class LIBPROTOBUF_EXPORT LocationTrackerInterface {
+ public:
+ virtual ~LocationTrackerInterface() {}
+
+ // Returns the object location as human readable string.
+ virtual string ToString() const = 0;
+
+ protected:
+ LocationTrackerInterface() {}
+
+ private:
+ // Please do not add any data members to this class.
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LocationTrackerInterface);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__
diff --git a/src/google/protobuf/util/internal/mock_error_listener.h b/src/google/protobuf/util/internal/mock_error_listener.h
new file mode 100644
index 00000000..591c35db
--- /dev/null
+++ b/src/google/protobuf/util/internal/mock_error_listener.h
@@ -0,0 +1,63 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__
+
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/location_tracker.h>
+#include <gmock/gmock.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class MockErrorListener : public ErrorListener {
+ public:
+ MockErrorListener() {}
+ virtual ~MockErrorListener() {}
+
+ MOCK_METHOD3(InvalidName, void(const LocationTrackerInterface& loc,
+ StringPiece unknown_name,
+ StringPiece message));
+ MOCK_METHOD3(InvalidValue, void(const LocationTrackerInterface& loc,
+ StringPiece type_name, StringPiece value));
+ MOCK_METHOD2(MissingField, void(const LocationTrackerInterface& loc,
+ StringPiece missing_name));
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__
diff --git a/src/google/protobuf/util/internal/object_location_tracker.h b/src/google/protobuf/util/internal/object_location_tracker.h
new file mode 100644
index 00000000..8586cecc
--- /dev/null
+++ b/src/google/protobuf/util/internal/object_location_tracker.h
@@ -0,0 +1,64 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_LOCATION_TRACKER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_LOCATION_TRACKER_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/location_tracker.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// An empty concrete implementation of LocationTrackerInterface.
+class ObjectLocationTracker : public LocationTrackerInterface {
+ public:
+ // Creates an empty location tracker.
+ ObjectLocationTracker() {}
+
+ virtual ~ObjectLocationTracker() {}
+
+ // Returns empty because nothing is tracked.
+ virtual string ToString() const { return ""; }
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectLocationTracker);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_LOCATION_TRACKER_H__
diff --git a/src/google/protobuf/util/internal/object_source.h b/src/google/protobuf/util/internal/object_source.h
new file mode 100644
index 00000000..2c31cfb0
--- /dev/null
+++ b/src/google/protobuf/util/internal/object_source.h
@@ -0,0 +1,79 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_SOURCE_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_SOURCE_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class ObjectWriter;
+
+// An ObjectSource is anything that can write to an ObjectWriter.
+// Implementation of this interface typically provide constructors or
+// factory methods to create an instance based on some source data, for
+// example, a character stream, or protobuf.
+//
+// Derived classes could be thread-unsafe.
+class LIBPROTOBUF_EXPORT ObjectSource {
+ public:
+ virtual ~ObjectSource() {}
+
+ // Writes to the ObjectWriter
+ virtual util::Status WriteTo(ObjectWriter* ow) const {
+ return NamedWriteTo("", ow);
+ }
+
+ // Writes to the ObjectWriter with a custom name for the message.
+ // This is useful when you chain ObjectSource together by embedding one
+ // within another.
+ virtual util::Status NamedWriteTo(StringPiece name,
+ ObjectWriter* ow) const = 0;
+
+ protected:
+ ObjectSource() {}
+
+ private:
+ // Do not add any data members to this class.
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectSource);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_SOURCE_H__
diff --git a/src/google/protobuf/util/internal/object_writer.cc b/src/google/protobuf/util/internal/object_writer.cc
new file mode 100644
index 00000000..57cc08a1
--- /dev/null
+++ b/src/google/protobuf/util/internal/object_writer.cc
@@ -0,0 +1,92 @@
+// 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 <google/protobuf/util/internal/object_writer.h>
+
+#include <google/protobuf/util/internal/datapiece.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// static
+void ObjectWriter::RenderDataPieceTo(const DataPiece& data, StringPiece name,
+ ObjectWriter* ow) {
+ switch (data.type()) {
+ case DataPiece::TYPE_INT32: {
+ ow->RenderInt32(name, data.ToInt32().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_INT64: {
+ ow->RenderInt64(name, data.ToInt64().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_UINT32: {
+ ow->RenderUint32(name, data.ToUint32().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_UINT64: {
+ ow->RenderUint64(name, data.ToUint64().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_DOUBLE: {
+ ow->RenderDouble(name, data.ToDouble().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_FLOAT: {
+ ow->RenderFloat(name, data.ToFloat().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_BOOL: {
+ ow->RenderBool(name, data.ToBool().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_STRING: {
+ ow->RenderString(name, data.ToString().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_BYTES: {
+ ow->RenderBytes(name, data.ToBytes().ValueOrDie());
+ break;
+ }
+ case DataPiece::TYPE_NULL: {
+ ow->RenderNull(name);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/object_writer.h b/src/google/protobuf/util/internal/object_writer.h
new file mode 100644
index 00000000..20bd3627
--- /dev/null
+++ b/src/google/protobuf/util/internal/object_writer.h
@@ -0,0 +1,126 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_WRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_WRITER_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class DataPiece;
+
+// An ObjectWriter is an interface for writing a stream of events
+// representing objects and collections. Implementation of this
+// interface can be used to write an object stream to an in-memory
+// structure, protobufs, JSON, XML, or any other output format
+// desired. The ObjectSource interface is typically used as the
+// source of an object stream.
+//
+// See JsonObjectWriter for a sample implementation of ObjectWriter
+// and its use.
+//
+// Derived classes could be thread-unsafe.
+//
+// TODO(xinb): seems like a prime candidate to apply the RAII paradigm
+// and get rid the need to call EndXXX().
+class LIBPROTOBUF_EXPORT ObjectWriter {
+ public:
+ virtual ~ObjectWriter() {}
+
+ // Starts an object. If the name is empty, the object will not be named.
+ virtual ObjectWriter* StartObject(StringPiece name) = 0;
+
+ // Ends an object.
+ virtual ObjectWriter* EndObject() = 0;
+
+ // Starts a list. If the name is empty, the list will not be named.
+ virtual ObjectWriter* StartList(StringPiece name) = 0;
+
+ // Ends a list.
+ virtual ObjectWriter* EndList() = 0;
+
+ // Renders a boolean value.
+ virtual ObjectWriter* RenderBool(StringPiece name, bool value) = 0;
+
+ // Renders an 32-bit integer value.
+ virtual ObjectWriter* RenderInt32(StringPiece name, int32 value) = 0;
+
+ // Renders an 32-bit unsigned integer value.
+ virtual ObjectWriter* RenderUint32(StringPiece name, uint32 value) = 0;
+
+ // Renders a 64-bit integer value.
+ virtual ObjectWriter* RenderInt64(StringPiece name, int64 value) = 0;
+
+ // Renders an 64-bit unsigned integer value.
+ virtual ObjectWriter* RenderUint64(StringPiece name, uint64 value) = 0;
+
+ // Renders a double value.
+ virtual ObjectWriter* RenderDouble(StringPiece name, double value) = 0;
+
+ // Renders a float value.
+ virtual ObjectWriter* RenderFloat(StringPiece name, float value) = 0;
+
+ // Renders a StringPiece value. This is for rendering strings.
+ virtual ObjectWriter* RenderString(StringPiece name, StringPiece value) = 0;
+
+ // Renders a bytes value.
+ virtual ObjectWriter* RenderBytes(StringPiece name, StringPiece value) = 0;
+
+ // Renders a Null value.
+ virtual ObjectWriter* RenderNull(StringPiece name) = 0;
+
+ // Disables case normalization. Any RenderTYPE call after calling this
+ // function will output the name field as-is. No normalization is attempted on
+ // it. This setting is reset immediately after the next RenderTYPE is called.
+ virtual ObjectWriter* DisableCaseNormalizationForNextKey() { return this; }
+
+ // Renders a DataPiece object to a ObjectWriter.
+ static void RenderDataPieceTo(const DataPiece& data, StringPiece name,
+ ObjectWriter* ow);
+
+ protected:
+ ObjectWriter() {}
+
+ private:
+ // Do not add any data members to this class.
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_WRITER_H__
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc
new file mode 100644
index 00000000..53a0e47a
--- /dev/null
+++ b/src/google/protobuf/util/internal/protostream_objectsource.cc
@@ -0,0 +1,1051 @@
+// 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 <google/protobuf/util/internal/protostream_objectsource.h>
+
+#include <utility>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/util/internal/field_mask_utility.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/status_macros.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+using util::Status;
+using util::StatusOr;
+namespace error {
+using util::error::Code;
+using util::error::INTERNAL;
+}
+namespace converter {
+
+using google::protobuf::Descriptor;
+using google::protobuf::EnumValueDescriptor;
+using google::protobuf::FieldDescriptor;
+using google::protobuf::internal::WireFormat;
+using google::protobuf::internal::WireFormatLite;
+using util::Status;
+using util::StatusOr;
+
+namespace {
+// Finds a field with the given number. NULL if none found.
+const google::protobuf::Field* FindFieldByNumber(
+ const google::protobuf::Type& type, int number);
+
+// Returns true if the field is packable.
+bool IsPackable(const google::protobuf::Field& field);
+
+// Finds an enum value with the given number. NULL if none found.
+const google::protobuf::EnumValue* FindEnumValueByNumber(
+ const google::protobuf::Enum& tech_enum, int number);
+
+// Utility function to format nanos.
+const string FormatNanos(uint32 nanos);
+} // namespace
+
+
+ProtoStreamObjectSource::ProtoStreamObjectSource(
+ google::protobuf::io::CodedInputStream* stream, TypeResolver* type_resolver,
+ const google::protobuf::Type& type)
+ : stream_(stream),
+ typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+ own_typeinfo_(true),
+ type_(type) {
+ GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL.";
+}
+
+ProtoStreamObjectSource::ProtoStreamObjectSource(
+ google::protobuf::io::CodedInputStream* stream, TypeInfo* typeinfo,
+ const google::protobuf::Type& type)
+ : stream_(stream), typeinfo_(typeinfo), own_typeinfo_(false), type_(type) {
+ GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL.";
+}
+
+ProtoStreamObjectSource::~ProtoStreamObjectSource() {
+ if (own_typeinfo_) {
+ delete typeinfo_;
+ }
+}
+
+Status ProtoStreamObjectSource::NamedWriteTo(StringPiece name,
+ ObjectWriter* ow) const {
+ return WriteMessage(type_, name, 0, true, ow);
+}
+
+const google::protobuf::Field* ProtoStreamObjectSource::FindAndVerifyField(
+ const google::protobuf::Type& type, uint32 tag) const {
+ // Lookup the new field in the type by tag number.
+ const google::protobuf::Field* field = FindFieldByNumber(type, tag >> 3);
+ // Verify if the field corresponds to the wire type in tag.
+ // If there is any discrepancy, mark the field as not found.
+ if (field != NULL) {
+ WireFormatLite::WireType expected_type =
+ WireFormatLite::WireTypeForFieldType(
+ static_cast<WireFormatLite::FieldType>(field->kind()));
+ WireFormatLite::WireType actual_type = WireFormatLite::GetTagWireType(tag);
+ if (actual_type != expected_type &&
+ (!IsPackable(*field) ||
+ actual_type != WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
+ field = NULL;
+ }
+ }
+ return field;
+}
+
+Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type,
+ StringPiece name,
+ const uint32 end_tag,
+ bool include_start_and_end,
+ ObjectWriter* ow) const {
+ const TypeRenderer* type_renderer = FindTypeRenderer(type.name());
+ if (type_renderer != NULL) {
+ return (*type_renderer)(this, type, name, ow);
+ }
+
+ const google::protobuf::Field* field = NULL;
+ string field_name;
+ // last_tag set to dummy value that is different from tag.
+ uint32 tag = stream_->ReadTag(), last_tag = tag + 1;
+
+ if (include_start_and_end) {
+ ow->StartObject(name);
+ }
+ while (tag != end_tag) {
+ if (tag != last_tag) { // Update field only if tag is changed.
+ last_tag = tag;
+ field = FindAndVerifyField(type, tag);
+ if (field != NULL) {
+ field_name = field->name();
+ }
+ }
+ if (field == NULL) {
+ // If we didn't find a field, skip this unknown tag.
+ // TODO(wpoon): Check return boolean value.
+ WireFormat::SkipField(stream_, tag, NULL);
+ tag = stream_->ReadTag();
+ continue;
+ }
+
+ if (field->cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
+ if (IsMap(*field)) {
+ ow->StartObject(field_name);
+ ASSIGN_OR_RETURN(tag, RenderMap(field, field_name, tag, ow));
+ ow->EndObject();
+ } else {
+ ASSIGN_OR_RETURN(tag, RenderList(field, field_name, tag, ow));
+ }
+ } else {
+ // Render the field.
+ RETURN_IF_ERROR(RenderField(field, field_name, ow));
+ tag = stream_->ReadTag();
+ }
+ }
+ if (include_start_and_end) {
+ ow->EndObject();
+ }
+ return Status::OK;
+}
+
+StatusOr<uint32> ProtoStreamObjectSource::RenderList(
+ const google::protobuf::Field* field, StringPiece name, uint32 list_tag,
+ ObjectWriter* ow) const {
+ uint32 tag_to_return = 0;
+ ow->StartList(name);
+ if (IsPackable(*field) &&
+ list_tag ==
+ WireFormatLite::MakeTag(field->number(),
+ WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
+ RETURN_IF_ERROR(RenderPacked(field, ow));
+ // Since packed fields have a single tag, read another tag from stream to
+ // return.
+ tag_to_return = stream_->ReadTag();
+ } else {
+ do {
+ RETURN_IF_ERROR(RenderField(field, "", ow));
+ } while ((tag_to_return = stream_->ReadTag()) == list_tag);
+ }
+ ow->EndList();
+ return tag_to_return;
+}
+
+StatusOr<uint32> ProtoStreamObjectSource::RenderMap(
+ const google::protobuf::Field* field, StringPiece name, uint32 list_tag,
+ ObjectWriter* ow) const {
+ const google::protobuf::Type* field_type =
+ typeinfo_->GetType(field->type_url());
+ uint32 tag_to_return = 0;
+ if (IsPackable(*field) &&
+ list_tag ==
+ WireFormatLite::MakeTag(field->number(),
+ WireFormatLite::WIRETYPE_LENGTH_DELIMITED)) {
+ RETURN_IF_ERROR(RenderPackedMapEntry(field_type, ow));
+ tag_to_return = stream_->ReadTag();
+ } else {
+ do {
+ RETURN_IF_ERROR(RenderMapEntry(field_type, ow));
+ } while ((tag_to_return = stream_->ReadTag()) == list_tag);
+ }
+ return tag_to_return;
+}
+
+Status ProtoStreamObjectSource::RenderMapEntry(
+ const google::protobuf::Type* type, ObjectWriter* ow) const {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // message length
+ int old_limit = stream_->PushLimit(buffer32);
+ string map_key;
+ for (uint32 tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
+ const google::protobuf::Field* field = FindAndVerifyField(*type, tag);
+ if (field == NULL) {
+ WireFormat::SkipField(stream_, tag, NULL);
+ continue;
+ }
+ // Map field numbers are key = 1 and value = 2
+ if (field->number() == 1) {
+ map_key = ReadFieldValueAsString(*field);
+ } else if (field->number() == 2) {
+ if (map_key.empty()) {
+ return Status(util::error::INTERNAL, "Map key must be non-empty");
+ }
+ // Disable case normalization for map keys as they are just data. We
+ // retain them intact.
+ ow->DisableCaseNormalizationForNextKey();
+ RETURN_IF_ERROR(RenderField(field, map_key, ow));
+ }
+ }
+ stream_->PopLimit(old_limit);
+
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderPacked(
+ const google::protobuf::Field* field, ObjectWriter* ow) const {
+ uint32 length;
+ stream_->ReadVarint32(&length);
+ int old_limit = stream_->PushLimit(length);
+ while (stream_->BytesUntilLimit() > 0) {
+ RETURN_IF_ERROR(RenderField(field, StringPiece(), ow));
+ }
+ stream_->PopLimit(old_limit);
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderPackedMapEntry(
+ const google::protobuf::Type* type, ObjectWriter* ow) const {
+ uint32 length;
+ stream_->ReadVarint32(&length);
+ int old_limit = stream_->PushLimit(length);
+ while (stream_->BytesUntilLimit() > 0) {
+ RETURN_IF_ERROR(RenderMapEntry(type, ow));
+ }
+ stream_->PopLimit(old_limit);
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderTimestamp(
+ const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+ StringPiece field_name, ObjectWriter* ow) {
+ pair<int64, int32> p = os->ReadSecondsAndNanos(type);
+ int64 seconds = p.first;
+ int32 nanos = p.second;
+ if (seconds > kMaxSeconds || seconds < kMinSeconds) {
+ return Status(
+ util::error::INTERNAL,
+ StrCat("Timestamp seconds exceeds limit for field: ", field_name));
+ }
+
+ if (nanos < 0 || nanos >= kNanosPerSecond) {
+ return Status(
+ util::error::INTERNAL,
+ StrCat("Timestamp nanos exceeds limit for field: ", field_name));
+ }
+
+ ow->RenderString(field_name,
+ ::google::protobuf::internal::FormatTime(seconds, nanos));
+
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderDuration(
+ const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+ StringPiece field_name, ObjectWriter* ow) {
+ pair<int64, int32> p = os->ReadSecondsAndNanos(type);
+ int64 seconds = p.first;
+ int32 nanos = p.second;
+ if (seconds > kMaxSeconds || seconds < kMinSeconds) {
+ return Status(
+ util::error::INTERNAL,
+ StrCat("Duration seconds exceeds limit for field: ", field_name));
+ }
+
+ if (nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
+ return Status(
+ util::error::INTERNAL,
+ StrCat("Duration nanos exceeds limit for field: ", field_name));
+ }
+
+ string sign = "";
+ if (seconds < 0) {
+ if (nanos > 0) {
+ return Status(util::error::INTERNAL,
+ StrCat(
+ "Duration nanos is non-negative, but seconds is "
+ "negative for field: ",
+ field_name));
+ }
+ sign = "-";
+ seconds = -seconds;
+ nanos = -nanos;
+ } else if (seconds == 0 && nanos < 0) {
+ sign = "-";
+ nanos = -nanos;
+ }
+ string formatted_duration = StringPrintf("%s%lld%ss", sign.c_str(), seconds,
+ FormatNanos(nanos).c_str());
+ ow->RenderString(field_name, formatted_duration);
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderDouble(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint64 buffer64 = 0; // default value of Double wrapper value
+ if (tag != 0) {
+ os->stream_->ReadLittleEndian64(&buffer64);
+ os->stream_->ReadTag();
+ }
+ ow->RenderDouble(field_name, bit_cast<double>(buffer64));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderFloat(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint32 buffer32 = 0; // default value of Float wrapper value
+ if (tag != 0) {
+ os->stream_->ReadLittleEndian32(&buffer32);
+ os->stream_->ReadTag();
+ }
+ ow->RenderFloat(field_name, bit_cast<float>(buffer32));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderInt64(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint64 buffer64 = 0; // default value of Int64 wrapper value
+ if (tag != 0) {
+ os->stream_->ReadVarint64(&buffer64);
+ os->stream_->ReadTag();
+ }
+ ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderUInt64(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint64 buffer64 = 0; // default value of UInt64 wrapper value
+ if (tag != 0) {
+ os->stream_->ReadVarint64(&buffer64);
+ os->stream_->ReadTag();
+ }
+ ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderInt32(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint32 buffer32 = 0; // default value of Int32 wrapper value
+ if (tag != 0) {
+ os->stream_->ReadVarint32(&buffer32);
+ os->stream_->ReadTag();
+ }
+ ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderUInt32(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint32 buffer32 = 0; // default value of UInt32 wrapper value
+ if (tag != 0) {
+ os->stream_->ReadVarint32(&buffer32);
+ os->stream_->ReadTag();
+ }
+ ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderBool(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint64 buffer64 = 0; // results in 'false' value as default, which is the
+ // default value of Bool wrapper
+ if (tag != 0) {
+ os->stream_->ReadVarint64(&buffer64);
+ os->stream_->ReadTag();
+ }
+ ow->RenderBool(field_name, buffer64 != 0);
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderString(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint32 buffer32;
+ string str; // default value of empty for String wrapper
+ if (tag != 0) {
+ os->stream_->ReadVarint32(&buffer32); // string size.
+ os->stream_->ReadString(&str, buffer32);
+ os->stream_->ReadTag();
+ }
+ ow->RenderString(field_name, str);
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderBytes(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+ uint32 buffer32;
+ string str;
+ if (tag != 0) {
+ os->stream_->ReadVarint32(&buffer32);
+ os->stream_->ReadString(&str, buffer32);
+ os->stream_->ReadTag();
+ }
+ ow->RenderBytes(field_name, str);
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderStruct(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ const google::protobuf::Field* field = NULL;
+ uint32 tag = os->stream_->ReadTag();
+ ow->StartObject(field_name);
+ while (tag != 0) {
+ field = os->FindAndVerifyField(type, tag);
+ // google.protobuf.Struct has only one field that is a map. Hence we use
+ // RenderMap to render that field.
+ if (os->IsMap(*field)) {
+ ASSIGN_OR_RETURN(tag, os->RenderMap(field, field_name, tag, ow));
+ }
+ }
+ ow->EndObject();
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderStructValue(
+ const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+ StringPiece field_name, ObjectWriter* ow) {
+ const google::protobuf::Field* field = NULL;
+ for (uint32 tag = os->stream_->ReadTag(); tag != 0;
+ tag = os->stream_->ReadTag()) {
+ field = os->FindAndVerifyField(type, tag);
+ if (field == NULL) {
+ WireFormat::SkipField(os->stream_, tag, NULL);
+ continue;
+ }
+ RETURN_IF_ERROR(os->RenderField(field, field_name, ow));
+ }
+ return Status::OK;
+}
+
+// TODO(skarvaje): Avoid code duplication of for loops and SkipField logic.
+Status ProtoStreamObjectSource::RenderStructListValue(
+ const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+ StringPiece field_name, ObjectWriter* ow) {
+ uint32 tag = os->stream_->ReadTag();
+
+ // Render empty list when we find empty ListValue message.
+ if (tag == 0) {
+ ow->StartList(field_name);
+ ow->EndList();
+ return Status::OK;
+ }
+
+ while (tag != 0) {
+ const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
+ if (field == NULL) {
+ WireFormat::SkipField(os->stream_, tag, NULL);
+ tag = os->stream_->ReadTag();
+ continue;
+ }
+ ASSIGN_OR_RETURN(tag, os->RenderList(field, field_name, tag, ow));
+ }
+ return Status::OK;
+}
+
+Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece field_name,
+ ObjectWriter* ow) {
+ // An Any is of the form { string type_url = 1; bytes value = 2; }
+ uint32 tag;
+ string type_url;
+ string value;
+
+ // First read out the type_url and value from the proto stream
+ for (tag = os->stream_->ReadTag(); tag != 0; tag = os->stream_->ReadTag()) {
+ const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
+ if (field == NULL) {
+ WireFormat::SkipField(os->stream_, tag, NULL);
+ continue;
+ }
+ // 'type_url' has field number of 1 and 'value' has field number 2
+ // //google/protobuf/any.proto
+ if (field->number() == 1) {
+ // read type_url
+ uint32 type_url_size;
+ os->stream_->ReadVarint32(&type_url_size);
+ os->stream_->ReadString(&type_url, type_url_size);
+ } else if (field->number() == 2) {
+ // read value
+ uint32 value_size;
+ os->stream_->ReadVarint32(&value_size);
+ os->stream_->ReadString(&value, value_size);
+ }
+ }
+
+ // If there is no value, we don't lookup the type, we just output it (if
+ // present). If both type and value are empty we output an empty object.
+ if (value.empty()) {
+ ow->StartObject(field_name);
+ if (!type_url.empty()) {
+ ow->RenderString("@type", type_url);
+ }
+ ow->EndObject();
+ return util::Status::OK;
+ }
+
+ // If there is a value but no type, we cannot render it, so report an error.
+ if (type_url.empty()) {
+ // TODO(sven): Add an external message once those are ready.
+ return util::Status(util::error::INTERNAL,
+ "Invalid Any, the type_url is missing.");
+ }
+
+ util::StatusOr<const google::protobuf::Type*> resolved_type =
+ os->typeinfo_->ResolveTypeUrl(type_url);
+
+ if (!resolved_type.ok()) {
+ // Convert into an internal error, since this means the backend gave us
+ // an invalid response (missing or invalid type information).
+ return util::Status(util::error::INTERNAL,
+ resolved_type.status().error_message());
+ }
+ // nested_type cannot be null at this time.
+ const google::protobuf::Type* nested_type = resolved_type.ValueOrDie();
+
+ // We know the type so we can render it. Recursively parse the nested stream
+ // using a nested ProtoStreamObjectSource using our nested type information.
+ google::protobuf::io::ArrayInputStream zero_copy_stream(value.data(), value.size());
+ google::protobuf::io::CodedInputStream in_stream(&zero_copy_stream);
+ ProtoStreamObjectSource nested_os(&in_stream, os->typeinfo_, *nested_type);
+
+ // We manually call start and end object here so we can inject the @type.
+ ow->StartObject(field_name);
+ ow->RenderString("@type", type_url);
+ util::Status result =
+ nested_os.WriteMessage(nested_os.type_, "value", 0, false, ow);
+ ow->EndObject();
+ return result;
+}
+
+Status ProtoStreamObjectSource::RenderFieldMask(
+ const ProtoStreamObjectSource* os, const google::protobuf::Type& type,
+ StringPiece field_name, ObjectWriter* ow) {
+ string combined;
+ uint32 buffer32;
+ uint32 paths_field_tag = 0;
+ for (uint32 tag = os->stream_->ReadTag(); tag != 0;
+ tag = os->stream_->ReadTag()) {
+ if (paths_field_tag == 0) {
+ const google::protobuf::Field* field = os->FindAndVerifyField(type, tag);
+ if (field != NULL && field->number() == 1 &&
+ field->name() == "paths") {
+ paths_field_tag = tag;
+ }
+ }
+ if (paths_field_tag != tag) {
+ return util::Status(util::error::INTERNAL,
+ "Invalid FieldMask, unexpected field.");
+ }
+ string str;
+ os->stream_->ReadVarint32(&buffer32); // string size.
+ os->stream_->ReadString(&str, buffer32);
+ if (!combined.empty()) {
+ combined.append(",");
+ }
+ combined.append(ConvertFieldMaskPath(str, &ToCamelCase));
+ }
+ ow->RenderString(field_name, combined);
+ return Status::OK;
+}
+
+hash_map<string, ProtoStreamObjectSource::TypeRenderer>*
+ProtoStreamObjectSource::CreateRendererMap() {
+ hash_map<string, ProtoStreamObjectSource::TypeRenderer>* result =
+ new hash_map<string, ProtoStreamObjectSource::TypeRenderer>();
+ (*result)["google.protobuf.Timestamp"] =
+ &ProtoStreamObjectSource::RenderTimestamp;
+ (*result)["google.protobuf.Duration"] =
+ &ProtoStreamObjectSource::RenderDuration;
+ (*result)["google.protobuf.DoubleValue"] =
+ &ProtoStreamObjectSource::RenderDouble;
+ (*result)["google.protobuf.FloatValue"] =
+ &ProtoStreamObjectSource::RenderFloat;
+ (*result)["google.protobuf.Int64Value"] =
+ &ProtoStreamObjectSource::RenderInt64;
+ (*result)["google.protobuf.UInt64Value"] =
+ &ProtoStreamObjectSource::RenderUInt64;
+ (*result)["google.protobuf.Int32Value"] =
+ &ProtoStreamObjectSource::RenderInt32;
+ (*result)["google.protobuf.UInt32Value"] =
+ &ProtoStreamObjectSource::RenderUInt32;
+ (*result)["google.protobuf.BoolValue"] = &ProtoStreamObjectSource::RenderBool;
+ (*result)["google.protobuf.StringValue"] =
+ &ProtoStreamObjectSource::RenderString;
+ (*result)["google.protobuf.BytesValue"] =
+ &ProtoStreamObjectSource::RenderBytes;
+ (*result)["google.protobuf.Any"] = &ProtoStreamObjectSource::RenderAny;
+ (*result)["google.protobuf.Struct"] = &ProtoStreamObjectSource::RenderStruct;
+ (*result)["google.protobuf.Value"] =
+ &ProtoStreamObjectSource::RenderStructValue;
+ (*result)["google.protobuf.ListValue"] =
+ &ProtoStreamObjectSource::RenderStructListValue;
+ (*result)["google.protobuf.FieldMask"] =
+ &ProtoStreamObjectSource::RenderFieldMask;
+ return result;
+}
+
+// static
+ProtoStreamObjectSource::TypeRenderer*
+ProtoStreamObjectSource::FindTypeRenderer(const string& type_url) {
+ static hash_map<string, TypeRenderer>* renderers = CreateRendererMap();
+ return FindOrNull(*renderers, type_url);
+}
+
+Status ProtoStreamObjectSource::RenderField(
+ const google::protobuf::Field* field, StringPiece field_name,
+ ObjectWriter* ow) const {
+ switch (field->kind()) {
+ case google::protobuf::Field_Kind_TYPE_BOOL: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ ow->RenderBool(field_name, buffer64 != 0);
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_INT32: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+ ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_INT64: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT32: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+ ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT64: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT32: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+ ow->RenderInt32(field_name, WireFormatLite::ZigZagDecode32(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT64: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ ow->RenderInt64(field_name, WireFormatLite::ZigZagDecode64(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED32: {
+ uint32 buffer32;
+ stream_->ReadLittleEndian32(&buffer32);
+ ow->RenderInt32(field_name, bit_cast<int32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED64: {
+ uint64 buffer64;
+ stream_->ReadLittleEndian64(&buffer64);
+ ow->RenderInt64(field_name, bit_cast<int64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED32: {
+ uint32 buffer32;
+ stream_->ReadLittleEndian32(&buffer32);
+ ow->RenderUint32(field_name, bit_cast<uint32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED64: {
+ uint64 buffer64;
+ stream_->ReadLittleEndian64(&buffer64);
+ ow->RenderUint64(field_name, bit_cast<uint64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FLOAT: {
+ uint32 buffer32;
+ stream_->ReadLittleEndian32(&buffer32);
+ ow->RenderFloat(field_name, bit_cast<float>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_DOUBLE: {
+ uint64 buffer64;
+ stream_->ReadLittleEndian64(&buffer64);
+ ow->RenderDouble(field_name, bit_cast<double>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_ENUM: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+
+ // If the field represents an explicit NULL value, render null.
+ if (field->type_url() == kStructNullValueTypeUrl) {
+ ow->RenderNull(field_name);
+ break;
+ }
+
+ // Get the nested enum type for this field.
+ // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
+ // up.
+ const google::protobuf::Enum* en = typeinfo_->GetEnum(field->type_url());
+ // Lookup the name of the enum, and render that. Skips unknown enums.
+ if (en != NULL) {
+ const google::protobuf::EnumValue* enum_value =
+ FindEnumValueByNumber(*en, buffer32);
+ if (enum_value != NULL) {
+ ow->RenderString(field_name, enum_value->name());
+ }
+ } else {
+ GOOGLE_LOG(INFO) << "Unkown enum skipped: " << field->type_url();
+ }
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_STRING: {
+ uint32 buffer32;
+ string str;
+ stream_->ReadVarint32(&buffer32); // string size.
+ stream_->ReadString(&str, buffer32);
+ ow->RenderString(field_name, str);
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BYTES: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // bytes size.
+ string value;
+ stream_->ReadString(&value, buffer32);
+ ow->RenderBytes(field_name, value);
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_MESSAGE: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // message length
+ int old_limit = stream_->PushLimit(buffer32);
+ // Get the nested message type for this field.
+ const google::protobuf::Type* type =
+ typeinfo_->GetType(field->type_url());
+ if (type == NULL) {
+ return Status(util::error::INTERNAL,
+ StrCat("Invalid configuration. Could not find the type: ",
+ field->type_url()));
+ }
+ RETURN_IF_ERROR(WriteMessage(*type, field_name, 0, true, ow));
+ if (!stream_->ConsumedEntireMessage()) {
+ return Status(util::error::INVALID_ARGUMENT,
+ "Nested protocol message not parsed in its entirety.");
+ }
+ stream_->PopLimit(old_limit);
+ break;
+ }
+ default:
+ break;
+ }
+ return Status::OK;
+}
+
+// TODO(skarvaje): Fix this to avoid code duplication.
+const string ProtoStreamObjectSource::ReadFieldValueAsString(
+ const google::protobuf::Field& field) const {
+ string result;
+ switch (field.kind()) {
+ case google::protobuf::Field_Kind_TYPE_BOOL: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ result = buffer64 != 0 ? "true" : "false";
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_INT32: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+ result = SimpleItoa(bit_cast<int32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_INT64: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ result = SimpleItoa(bit_cast<int64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT32: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+ result = SimpleItoa(bit_cast<uint32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT64: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ result = SimpleItoa(bit_cast<uint64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT32: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+ result = SimpleItoa(WireFormatLite::ZigZagDecode32(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT64: {
+ uint64 buffer64;
+ stream_->ReadVarint64(&buffer64);
+ result = SimpleItoa(WireFormatLite::ZigZagDecode64(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED32: {
+ uint32 buffer32;
+ stream_->ReadLittleEndian32(&buffer32);
+ result = SimpleItoa(bit_cast<int32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED64: {
+ uint64 buffer64;
+ stream_->ReadLittleEndian64(&buffer64);
+ result = SimpleItoa(bit_cast<int64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED32: {
+ uint32 buffer32;
+ stream_->ReadLittleEndian32(&buffer32);
+ result = SimpleItoa(bit_cast<uint32>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED64: {
+ uint64 buffer64;
+ stream_->ReadLittleEndian64(&buffer64);
+ result = SimpleItoa(bit_cast<uint64>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FLOAT: {
+ uint32 buffer32;
+ stream_->ReadLittleEndian32(&buffer32);
+ result = SimpleFtoa(bit_cast<float>(buffer32));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_DOUBLE: {
+ uint64 buffer64;
+ stream_->ReadLittleEndian64(&buffer64);
+ result = SimpleDtoa(bit_cast<double>(buffer64));
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_ENUM: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32);
+ // Get the nested enum type for this field.
+ // TODO(skarvaje): Avoid string manipulation. Find ways to speed this
+ // up.
+ const google::protobuf::Enum* en = typeinfo_->GetEnum(field.type_url());
+ // Lookup the name of the enum, and render that. Skips unknown enums.
+ if (en != NULL) {
+ const google::protobuf::EnumValue* enum_value =
+ FindEnumValueByNumber(*en, buffer32);
+ if (enum_value != NULL) {
+ result = enum_value->name();
+ }
+ }
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_STRING: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // string size.
+ stream_->ReadString(&result, buffer32);
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BYTES: {
+ uint32 buffer32;
+ stream_->ReadVarint32(&buffer32); // bytes size.
+ stream_->ReadString(&result, buffer32);
+ break;
+ }
+ default:
+ break;
+ }
+ return result;
+}
+
+// Field is a map if it is a repeated message and it has an option "map_type".
+// TODO(skarvaje): Consider pre-computing the IsMap() into Field directly.
+bool ProtoStreamObjectSource::IsMap(
+ const google::protobuf::Field& field) const {
+ const google::protobuf::Type* field_type =
+ typeinfo_->GetType(field.type_url());
+
+ // TODO(xiaofeng): Unify option names.
+ return field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE &&
+ (GetBoolOptionOrDefault(field_type->options(),
+ "google.protobuf.MessageOptions.map_entry", false) ||
+ GetBoolOptionOrDefault(field_type->options(), "map_entry", false));
+}
+
+std::pair<int64, int32> ProtoStreamObjectSource::ReadSecondsAndNanos(
+ const google::protobuf::Type& type) const {
+ uint64 seconds = 0;
+ uint32 nanos = 0;
+ uint32 tag = 0;
+ int64 signed_seconds = 0;
+ int64 signed_nanos = 0;
+
+ for (tag = stream_->ReadTag(); tag != 0; tag = stream_->ReadTag()) {
+ const google::protobuf::Field* field = FindAndVerifyField(type, tag);
+ if (field == NULL) {
+ WireFormat::SkipField(stream_, tag, NULL);
+ continue;
+ }
+ // 'seconds' has field number of 1 and 'nanos' has field number 2
+ // //google/protobuf/timestamp.proto & duration.proto
+ if (field->number() == 1) {
+ // read seconds
+ stream_->ReadVarint64(&seconds);
+ signed_seconds = bit_cast<int64>(seconds);
+ } else if (field->number() == 2) {
+ // read nanos
+ stream_->ReadVarint32(&nanos);
+ signed_nanos = bit_cast<int32>(nanos);
+ }
+ }
+ return std::pair<int64, int32>(signed_seconds, signed_nanos);
+}
+
+namespace {
+// TODO(skarvaje): Speed this up by not doing a linear scan.
+const google::protobuf::Field* FindFieldByNumber(
+ const google::protobuf::Type& type, int number) {
+ for (int i = 0; i < type.fields_size(); ++i) {
+ if (type.fields(i).number() == number) {
+ return &type.fields(i);
+ }
+ }
+ return NULL;
+}
+
+// TODO(skarvaje): Replace FieldDescriptor by implementing IsTypePackable()
+// using tech Field.
+bool IsPackable(const google::protobuf::Field& field) {
+ return field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED &&
+ google::protobuf::FieldDescriptor::IsTypePackable(
+ static_cast<google::protobuf::FieldDescriptor::Type>(field.kind()));
+}
+
+// TODO(skarvaje): Speed this up by not doing a linear scan.
+const google::protobuf::EnumValue* FindEnumValueByNumber(
+ const google::protobuf::Enum& tech_enum, int number) {
+ for (int i = 0; i < tech_enum.enumvalue_size(); ++i) {
+ const google::protobuf::EnumValue& ev = tech_enum.enumvalue(i);
+ if (ev.number() == number) {
+ return &ev;
+ }
+ }
+ return NULL;
+}
+
+// TODO(skarvaje): Look into optimizing this by not doing computation on
+// double.
+const string FormatNanos(uint32 nanos) {
+ const char* format =
+ (nanos % 1000 != 0) ? "%.9f" : (nanos % 1000000 != 0) ? "%.6f" : "%.3f";
+ string formatted =
+ StringPrintf(format, static_cast<double>(nanos) / kNanosPerSecond);
+ // remove the leading 0 before decimal.
+ return formatted.substr(1);
+}
+} // namespace
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h
new file mode 100644
index 00000000..4a4e6bbf
--- /dev/null
+++ b/src/google/protobuf/util/internal/protostream_objectsource.h
@@ -0,0 +1,245 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTSOURCE_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTSOURCE_H__
+
+#include <functional>
+#include <google/protobuf/stubs/hash.h>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/util/internal/object_source.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+
+
+
+namespace google {
+namespace protobuf {
+class Field;
+class Type;
+} // namespace protobuf
+
+
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class TypeInfo;
+
+// An ObjectSource that can parse a stream of bytes as a protocol buffer.
+// This implementation uses a tech Type for tag lookup.
+//
+// Sample usage: (suppose input is: string proto)
+// ArrayInputStream arr_stream(proto.data(), proto.size());
+// CodedInputStream in_stream(&arr_stream);
+// ProtoStreamObjectSource os(&in_stream, /*ServiceTypeInfo*/ typeinfo,
+// <your message google::protobuf::Type>);
+//
+// Status status = os.WriteTo(<some ObjectWriter>);
+class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource {
+ public:
+ ProtoStreamObjectSource(google::protobuf::io::CodedInputStream* stream,
+ TypeResolver* type_resolver,
+ const google::protobuf::Type& type);
+
+ virtual ~ProtoStreamObjectSource();
+
+ virtual util::Status NamedWriteTo(StringPiece name, ObjectWriter* ow) const;
+
+ protected:
+ // Writes a proto2 Message to the ObjectWriter. When the given end_tag is
+ // found this method will complete, allowing it to be used for parsing both
+ // nested messages (end with 0) and nested groups (end with group end tag).
+ // The include_start_and_end parameter allows this method to be called when
+ // already inside of an object, and skip calling StartObject and EndObject.
+ virtual util::Status WriteMessage(const google::protobuf::Type& descriptor,
+ StringPiece name, const uint32 end_tag,
+ bool include_start_and_end,
+ ObjectWriter* ow) const;
+
+ private:
+ ProtoStreamObjectSource(google::protobuf::io::CodedInputStream* stream,
+ TypeInfo* typeinfo,
+ const google::protobuf::Type& type);
+ // Function that renders a well known type with a modified behavior.
+ typedef util::Status (*TypeRenderer)(const ProtoStreamObjectSource*,
+ const google::protobuf::Type&,
+ StringPiece, ObjectWriter*);
+
+ // Looks up a field and verify its consistency with wire type in tag.
+ const google::protobuf::Field* FindAndVerifyField(
+ const google::protobuf::Type& type, uint32 tag) const;
+
+ // TODO(skarvaje): Mark these methods as non-const as they modify internal
+ // state (stream_).
+ //
+ // Renders a repeating field (packed or unpacked).
+ // Returns the next tag after reading all sequential repeating elements. The
+ // caller should use this tag before reading more tags from the stream.
+ util::StatusOr<uint32> RenderList(const google::protobuf::Field* field,
+ StringPiece name, uint32 list_tag,
+ ObjectWriter* ow) const;
+ // Renders a NWP map.
+ // Returns the next tag after reading all map entries. The caller should use
+ // this tag before reading more tags from the stream.
+ util::StatusOr<uint32> RenderMap(const google::protobuf::Field* field,
+ StringPiece name, uint32 list_tag,
+ ObjectWriter* ow) const;
+
+ // Renders an entry in a map, advancing stream pointers appropriately.
+ util::Status RenderMapEntry(const google::protobuf::Type* type,
+ ObjectWriter* ow) const;
+
+ // Renders a packed repeating field. A packed field is stored as:
+ // {tag length item1 item2 item3} instead of the less efficient
+ // {tag item1 tag item2 tag item3}.
+ util::Status RenderPacked(const google::protobuf::Field* field,
+ ObjectWriter* ow) const;
+
+ // Equivalent of RenderPacked, but for map entries.
+ util::Status RenderPackedMapEntry(const google::protobuf::Type* type,
+ ObjectWriter* ow) const;
+
+ // Renders a google.protobuf.Timestamp value to ObjectWriter
+ static util::Status RenderTimestamp(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ // Renders a google.protobuf.Duration value to ObjectWriter
+ static util::Status RenderDuration(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ // Following RenderTYPE functions render well known types in
+ // google/protobuf/wrappers.proto corresponding to TYPE.
+ static util::Status RenderDouble(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderFloat(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderInt64(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderUInt64(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderInt32(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderUInt32(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderBool(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderString(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+ static util::Status RenderBytes(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ // Renders a google.protobuf.Struct to ObjectWriter.
+ static util::Status RenderStruct(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ // Helper to render google.protobuf.Struct's Value fields to ObjectWriter.
+ static util::Status RenderStructValue(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ // Helper to render google.protobuf.Struct's ListValue fields to ObjectWriter.
+ static util::Status RenderStructListValue(
+ const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ // Render the "Any" type.
+ static util::Status RenderAny(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ // Render the "FieldMask" type.
+ static util::Status RenderFieldMask(const ProtoStreamObjectSource* os,
+ const google::protobuf::Type& type,
+ StringPiece name, ObjectWriter* ow);
+
+ static hash_map<string, TypeRenderer>* CreateRendererMap();
+ static TypeRenderer* FindTypeRenderer(const string& type_url);
+
+ // Renders a field value to the ObjectWriter.
+ util::Status RenderField(const google::protobuf::Field* field,
+ StringPiece field_name, ObjectWriter* ow) const;
+
+ // Reads field value according to Field spec in 'field' and returns the read
+ // value as string. This only works for primitive datatypes (no message
+ // types).
+ const string ReadFieldValueAsString(
+ const google::protobuf::Field& field) const;
+
+ // Utility function to detect proto maps. The 'field' MUST be repeated.
+ bool IsMap(const google::protobuf::Field& field) const;
+
+ // Utility to read int64 and int32 values from a message type in stream_.
+ // Used for reading google.protobuf.Timestamp and Duration messages.
+ std::pair<int64, int32> ReadSecondsAndNanos(
+ const google::protobuf::Type& type) const;
+
+ // Input stream to read from. Ownership rests with the caller.
+ google::protobuf::io::CodedInputStream* stream_;
+
+ // Type information for all the types used in the descriptor. Used to find
+ // google::protobuf::Type of nested messages/enums.
+ TypeInfo* typeinfo_;
+ // Whether this class owns the typeinfo_ object. If true the typeinfo_ object
+ // should be deleted in the destructor.
+ bool own_typeinfo_;
+
+ // google::protobuf::Type of the message source.
+ const google::protobuf::Type& type_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectSource);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTSOURCE_H__
diff --git a/src/google/protobuf/util/internal/protostream_objectsource_test.cc b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
new file mode 100644
index 00000000..4cc62410
--- /dev/null
+++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc
@@ -0,0 +1,824 @@
+// 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 <google/protobuf/util/internal/protostream_objectsource.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <sstream>
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/any.pb.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/expecting_objectwriter.h>
+#include <google/protobuf/util/internal/testdata/books.pb.h>
+#include <google/protobuf/util/internal/testdata/field_mask.pb.h>
+#include <google/protobuf/util/internal/type_info_test_helper.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/testdata/anys.pb.h>
+#include <google/protobuf/util/internal/testdata/maps.pb.h>
+#include <google/protobuf/util/internal/testdata/struct.pb.h>
+#include <gtest/gtest.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using google::protobuf::Descriptor;
+using google::protobuf::DescriptorPool;
+using google::protobuf::FileDescriptorProto;
+using google::protobuf::Message;
+using google::protobuf::io::ArrayInputStream;
+using google::protobuf::io::CodedInputStream;
+using util::Status;
+using google::protobuf::testing::Author;
+using google::protobuf::testing::BadAuthor;
+using google::protobuf::testing::BadNestedBook;
+using google::protobuf::testing::Book;
+using google::protobuf::testing::Book_Label;
+using google::protobuf::testing::NestedBook;
+using google::protobuf::testing::PackedPrimitive;
+using google::protobuf::testing::Primitive;
+using google::protobuf::testing::more_author;
+using google::protobuf::testing::maps::MapOut;
+using google::protobuf::testing::anys::AnyOut;
+using google::protobuf::testing::anys::AnyM;
+using google::protobuf::testing::FieldMaskTest;
+using google::protobuf::testing::NestedFieldMask;
+using google::protobuf::testing::structs::StructType;
+using ::testing::_;
+
+
+namespace {
+string GetTypeUrl(const Descriptor* descriptor) {
+ return string(kTypeServiceBaseUrl) + "/" + descriptor->full_name();
+}
+} // namespace
+
+class ProtostreamObjectSourceTest
+ : public ::testing::TestWithParam<testing::TypeInfoSource> {
+ protected:
+ ProtostreamObjectSourceTest() : helper_(GetParam()), mock_(), ow_(&mock_) {
+ helper_.ResetTypeInfo(Book::descriptor());
+ }
+
+ virtual ~ProtostreamObjectSourceTest() {}
+
+ void DoTest(const Message& msg, const Descriptor* descriptor) {
+ Status status = ExecuteTest(msg, descriptor);
+ EXPECT_EQ(Status::OK, status);
+ }
+
+ Status ExecuteTest(const Message& msg, const Descriptor* descriptor) {
+ ostringstream oss;
+ msg.SerializePartialToOstream(&oss);
+ string proto = oss.str();
+ ArrayInputStream arr_stream(proto.data(), proto.size());
+ CodedInputStream in_stream(&arr_stream);
+
+ google::protobuf::scoped_ptr<ProtoStreamObjectSource> os(
+ helper_.NewProtoSource(&in_stream, GetTypeUrl(descriptor)));
+ return os->WriteTo(&mock_);
+ }
+
+ void PrepareExpectingObjectWriterForRepeatedPrimitive() {
+ ow_.StartObject("")
+ ->StartList("rep_fix32")
+ ->RenderUint32("", bit_cast<uint32>(3201))
+ ->RenderUint32("", bit_cast<uint32>(0))
+ ->RenderUint32("", bit_cast<uint32>(3202))
+ ->EndList()
+ ->StartList("rep_u32")
+ ->RenderUint32("", bit_cast<uint32>(3203))
+ ->RenderUint32("", bit_cast<uint32>(0))
+ ->EndList()
+ ->StartList("rep_i32")
+ ->RenderInt32("", 0)
+ ->RenderInt32("", 3204)
+ ->RenderInt32("", 3205)
+ ->EndList()
+ ->StartList("rep_sf32")
+ ->RenderInt32("", 3206)
+ ->RenderInt32("", 0)
+ ->EndList()
+ ->StartList("rep_s32")
+ ->RenderInt32("", 0)
+ ->RenderInt32("", 3207)
+ ->RenderInt32("", 3208)
+ ->EndList()
+ ->StartList("rep_fix64")
+ ->RenderUint64("", bit_cast<uint64>(6401LL))
+ ->RenderUint64("", bit_cast<uint64>(0LL))
+ ->EndList()
+ ->StartList("rep_u64")
+ ->RenderUint64("", bit_cast<uint64>(0LL))
+ ->RenderUint64("", bit_cast<uint64>(6402LL))
+ ->RenderUint64("", bit_cast<uint64>(6403LL))
+ ->EndList()
+ ->StartList("rep_i64")
+ ->RenderInt64("", 6404L)
+ ->RenderInt64("", 0L)
+ ->EndList()
+ ->StartList("rep_sf64")
+ ->RenderInt64("", 0L)
+ ->RenderInt64("", 6405L)
+ ->RenderInt64("", 6406L)
+ ->EndList()
+ ->StartList("rep_s64")
+ ->RenderInt64("", 6407L)
+ ->RenderInt64("", 0L)
+ ->EndList()
+ ->StartList("rep_float")
+ ->RenderFloat("", 0.0f)
+ ->RenderFloat("", 32.1f)
+ ->RenderFloat("", 32.2f)
+ ->EndList()
+ ->StartList("rep_double")
+ ->RenderDouble("", 64.1L)
+ ->RenderDouble("", 0.0L)
+ ->EndList()
+ ->StartList("rep_bool")
+ ->RenderBool("", true)
+ ->RenderBool("", false)
+ ->EndList()
+ ->EndObject();
+ }
+
+ Primitive PrepareRepeatedPrimitive() {
+ Primitive primitive;
+ primitive.add_rep_fix32(3201);
+ primitive.add_rep_fix32(0);
+ primitive.add_rep_fix32(3202);
+ primitive.add_rep_u32(3203);
+ primitive.add_rep_u32(0);
+ primitive.add_rep_i32(0);
+ primitive.add_rep_i32(3204);
+ primitive.add_rep_i32(3205);
+ primitive.add_rep_sf32(3206);
+ primitive.add_rep_sf32(0);
+ primitive.add_rep_s32(0);
+ primitive.add_rep_s32(3207);
+ primitive.add_rep_s32(3208);
+ primitive.add_rep_fix64(6401L);
+ primitive.add_rep_fix64(0L);
+ primitive.add_rep_u64(0L);
+ primitive.add_rep_u64(6402L);
+ primitive.add_rep_u64(6403L);
+ primitive.add_rep_i64(6404L);
+ primitive.add_rep_i64(0L);
+ primitive.add_rep_sf64(0L);
+ primitive.add_rep_sf64(6405L);
+ primitive.add_rep_sf64(6406L);
+ primitive.add_rep_s64(6407L);
+ primitive.add_rep_s64(0L);
+ primitive.add_rep_float(0.0f);
+ primitive.add_rep_float(32.1f);
+ primitive.add_rep_float(32.2f);
+ primitive.add_rep_double(64.1L);
+ primitive.add_rep_double(0.0);
+ primitive.add_rep_bool(true);
+ primitive.add_rep_bool(false);
+
+ PrepareExpectingObjectWriterForRepeatedPrimitive();
+ return primitive;
+ }
+
+ PackedPrimitive PreparePackedPrimitive() {
+ PackedPrimitive primitive;
+ primitive.add_rep_fix32(3201);
+ primitive.add_rep_fix32(0);
+ primitive.add_rep_fix32(3202);
+ primitive.add_rep_u32(3203);
+ primitive.add_rep_u32(0);
+ primitive.add_rep_i32(0);
+ primitive.add_rep_i32(3204);
+ primitive.add_rep_i32(3205);
+ primitive.add_rep_sf32(3206);
+ primitive.add_rep_sf32(0);
+ primitive.add_rep_s32(0);
+ primitive.add_rep_s32(3207);
+ primitive.add_rep_s32(3208);
+ primitive.add_rep_fix64(6401L);
+ primitive.add_rep_fix64(0L);
+ primitive.add_rep_u64(0L);
+ primitive.add_rep_u64(6402L);
+ primitive.add_rep_u64(6403L);
+ primitive.add_rep_i64(6404L);
+ primitive.add_rep_i64(0L);
+ primitive.add_rep_sf64(0L);
+ primitive.add_rep_sf64(6405L);
+ primitive.add_rep_sf64(6406L);
+ primitive.add_rep_s64(6407L);
+ primitive.add_rep_s64(0L);
+ primitive.add_rep_float(0.0f);
+ primitive.add_rep_float(32.1f);
+ primitive.add_rep_float(32.2f);
+ primitive.add_rep_double(64.1L);
+ primitive.add_rep_double(0.0);
+ primitive.add_rep_bool(true);
+ primitive.add_rep_bool(false);
+
+ PrepareExpectingObjectWriterForRepeatedPrimitive();
+ return primitive;
+ }
+
+ testing::TypeInfoTestHelper helper_;
+
+ ::testing::NiceMock<MockObjectWriter> mock_;
+ ExpectingObjectWriter ow_;
+};
+
+INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
+ ProtostreamObjectSourceTest,
+ ::testing::Values(
+ testing::USE_TYPE_RESOLVER));
+
+TEST_P(ProtostreamObjectSourceTest, EmptyMessage) {
+ Book empty;
+ ow_.StartObject("")->EndObject();
+ DoTest(empty, Book::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, Primitives) {
+ Primitive primitive;
+ primitive.set_fix32(3201);
+ primitive.set_u32(3202);
+ primitive.set_i32(3203);
+ primitive.set_sf32(3204);
+ primitive.set_s32(3205);
+ primitive.set_fix64(6401L);
+ primitive.set_u64(6402L);
+ primitive.set_i64(6403L);
+ primitive.set_sf64(6404L);
+ primitive.set_s64(6405L);
+ primitive.set_str("String Value");
+ primitive.set_bytes("Some Bytes");
+ primitive.set_float_(32.1f);
+ primitive.set_double_(64.1L);
+ primitive.set_bool_(true);
+
+ ow_.StartObject("")
+ ->RenderUint32("fix32", bit_cast<uint32>(3201))
+ ->RenderUint32("u32", bit_cast<uint32>(3202))
+ ->RenderInt32("i32", 3203)
+ ->RenderInt32("sf32", 3204)
+ ->RenderInt32("s32", 3205)
+ ->RenderUint64("fix64", bit_cast<uint64>(6401LL))
+ ->RenderUint64("u64", bit_cast<uint64>(6402LL))
+ ->RenderInt64("i64", 6403L)
+ ->RenderInt64("sf64", 6404L)
+ ->RenderInt64("s64", 6405L)
+ ->RenderString("str", "String Value")
+ ->RenderBytes("bytes", "Some Bytes")
+ ->RenderFloat("float", 32.1f)
+ ->RenderDouble("double", 64.1L)
+ ->RenderBool("bool", true)
+ ->EndObject();
+ DoTest(primitive, Primitive::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) {
+ Primitive primitive = PrepareRepeatedPrimitive();
+ primitive.add_rep_str("String One");
+ primitive.add_rep_str("String Two");
+ primitive.add_rep_bytes("Some Bytes");
+
+ ow_.StartList("rep_str")
+ ->RenderString("", "String One")
+ ->RenderString("", "String Two")
+ ->EndList()
+ ->StartList("rep_bytes")
+ ->RenderBytes("", "Some Bytes")
+ ->EndList();
+ DoTest(primitive, Primitive::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, NestedMessage) {
+ Author* author = new Author();
+ author->set_id(101L);
+ author->set_name("Tolstoy");
+ Book book;
+ book.set_title("My Book");
+ book.set_allocated_author(author);
+
+ ow_.StartObject("")
+ ->RenderString("title", "My Book")
+ ->StartObject("author")
+ ->RenderUint64("id", bit_cast<uint64>(101LL))
+ ->RenderString("name", "Tolstoy")
+ ->EndObject()
+ ->EndObject();
+ DoTest(book, Book::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, RepeatingField) {
+ Author author;
+ author.set_alive(false);
+ author.set_name("john");
+ author.add_pseudonym("phil");
+ author.add_pseudonym("bob");
+
+ ow_.StartObject("")
+ ->RenderBool("alive", false)
+ ->RenderString("name", "john")
+ ->StartList("pseudonym")
+ ->RenderString("", "phil")
+ ->RenderString("", "bob")
+ ->EndList()
+ ->EndObject();
+ DoTest(author, Author::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, PackedRepeatingFields) {
+ DoTest(PreparePackedPrimitive(), PackedPrimitive::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, NonPackedPackableFieldsActuallyPacked) {
+ // Protostream is packed, but parse with non-packed Primitive.
+ DoTest(PreparePackedPrimitive(), Primitive::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, PackedPackableFieldNotActuallyPacked) {
+ // Protostream is not packed, but parse with PackedPrimitive.
+ DoTest(PrepareRepeatedPrimitive(), PackedPrimitive::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, BadAuthor) {
+ Author author;
+ author.set_alive(false);
+ author.set_name("john");
+ author.set_id(1234L);
+ author.add_pseudonym("phil");
+ author.add_pseudonym("bob");
+
+ ow_.StartObject("")
+ ->StartList("alive")
+ ->RenderBool("", false)
+ ->EndList()
+ ->StartList("name")
+ ->RenderUint64("", static_cast<uint64>('j'))
+ ->RenderUint64("", static_cast<uint64>('o'))
+ ->RenderUint64("", static_cast<uint64>('h'))
+ ->RenderUint64("", static_cast<uint64>('n'))
+ ->EndList()
+ ->RenderString("pseudonym", "phil")
+ ->RenderString("pseudonym", "bob")
+ ->EndObject();
+ // Protostream created with Author, but parsed with BadAuthor.
+ DoTest(author, BadAuthor::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, NestedBookToBadNestedBook) {
+ Book* book = new Book();
+ book->set_length(250);
+ book->set_published(2014L);
+ NestedBook nested;
+ nested.set_allocated_book(book);
+
+ ow_.StartObject("")
+ ->StartList("book")
+ ->RenderUint32("", 24) // tag for field length (3 << 3)
+ ->RenderUint32("", 250)
+ ->RenderUint32("", 32) // tag for field published (4 << 3)
+ ->RenderUint32("", 2014)
+ ->EndList()
+ ->EndObject();
+ // Protostream created with NestedBook, but parsed with BadNestedBook.
+ DoTest(nested, BadNestedBook::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest, BadNestedBookToNestedBook) {
+ BadNestedBook nested;
+ nested.add_book(1);
+ nested.add_book(2);
+ nested.add_book(3);
+ nested.add_book(4);
+ nested.add_book(5);
+ nested.add_book(6);
+ nested.add_book(7);
+
+ ow_.StartObject("")->StartObject("book")->EndObject()->EndObject();
+ // Protostream created with BadNestedBook, but parsed with NestedBook.
+ DoTest(nested, NestedBook::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceTest,
+ LongRepeatedListDoesNotBreakIntoMultipleJsonLists) {
+ Book book;
+
+ int repeat = 10000;
+ for (int i = 0; i < repeat; ++i) {
+ Book_Label* label = book.add_labels();
+ label->set_key(StrCat("i", i));
+ label->set_value(StrCat("v", i));
+ }
+
+ // Make sure StartList and EndList are called exactly once (see b/18227499 for
+ // problems when this doesn't happen)
+ EXPECT_CALL(mock_, StartList(_)).Times(1);
+ EXPECT_CALL(mock_, EndList()).Times(1);
+
+ DoTest(book, Book::descriptor());
+}
+
+class ProtostreamObjectSourceMapsTest : public ProtostreamObjectSourceTest {
+ protected:
+ ProtostreamObjectSourceMapsTest() {
+ helper_.ResetTypeInfo(MapOut::descriptor());
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
+ ProtostreamObjectSourceMapsTest,
+ ::testing::Values(
+ testing::USE_TYPE_RESOLVER));
+
+// Tests JSON map.
+//
+// This is the example expected output.
+// {
+// "map1": {
+// "key1": {
+// "foo": "foovalue"
+// },
+// "key2": {
+// "foo": "barvalue"
+// }
+// },
+// "map2": {
+// "nestedself": {
+// "map1": {
+// "nested_key1": {
+// "foo": "nested_foo"
+// }
+// },
+// "bar": "nested_bar_string"
+// }
+// },
+// "map3": {
+// "111": "one one one"
+// },
+// "bar": "top bar"
+// }
+TEST_P(ProtostreamObjectSourceMapsTest, MapsTest) {
+ MapOut out;
+ (*out.mutable_map1())["key1"].set_foo("foovalue");
+ (*out.mutable_map1())["key2"].set_foo("barvalue");
+
+ MapOut* nested_value = &(*out.mutable_map2())["nestedself"];
+ (*nested_value->mutable_map1())["nested_key1"].set_foo("nested_foo");
+ nested_value->set_bar("nested_bar_string");
+
+ (*out.mutable_map3())[111] = "one one one";
+
+ out.set_bar("top bar");
+
+ ow_.StartObject("")
+ ->StartObject("map1")
+ ->StartObject("key1")
+ ->RenderString("foo", "foovalue")
+ ->EndObject()
+ ->StartObject("key2")
+ ->RenderString("foo", "barvalue")
+ ->EndObject()
+ ->StartObject("map2")
+ ->StartObject("nestedself")
+ ->StartObject("map1")
+ ->StartObject("nested_key1")
+ ->RenderString("foo", "nested_foo")
+ ->EndObject()
+ ->EndObject()
+ ->RenderString("bar", "nested_bar_string")
+ ->EndObject()
+ ->EndObject()
+ ->StartObject("map3")
+ ->RenderString("111", "one one one")
+ ->EndObject()
+ ->EndObject()
+ ->RenderString("bar", "top bar")
+ ->EndObject();
+
+ DoTest(out, MapOut::descriptor());
+}
+
+class ProtostreamObjectSourceAnysTest : public ProtostreamObjectSourceTest {
+ protected:
+ ProtostreamObjectSourceAnysTest() {
+ helper_.ResetTypeInfo(AnyOut::descriptor(),
+ google::protobuf::Any::descriptor());
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
+ ProtostreamObjectSourceAnysTest,
+ ::testing::Values(
+ testing::USE_TYPE_RESOLVER));
+
+// Tests JSON any support.
+//
+// This is the example expected output.
+// {
+// "any": {
+// "@type": "type.googleapis.com/google.protobuf.testing.anys.AnyM"
+// "foo": "foovalue"
+// }
+// }
+TEST_P(ProtostreamObjectSourceAnysTest, BasicAny) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+
+ AnyM m;
+ m.set_foo("foovalue");
+ any->PackFrom(m);
+
+ ow_.StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type",
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM")
+ ->RenderString("foo", "foovalue")
+ ->EndObject()
+ ->EndObject();
+
+ DoTest(out, AnyOut::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceAnysTest, RecursiveAny) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+ any->set_type_url("type.googleapis.com/google.protobuf.Any");
+
+ ::google::protobuf::Any nested_any;
+ nested_any.set_type_url(
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM");
+
+ AnyM m;
+ m.set_foo("foovalue");
+ nested_any.set_value(m.SerializeAsString());
+
+ any->set_value(nested_any.SerializeAsString());
+
+ ow_.StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
+ ->StartObject("value")
+ ->RenderString("@type",
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM")
+ ->RenderString("foo", "foovalue")
+ ->EndObject()
+ ->EndObject()
+ ->EndObject();
+
+ DoTest(out, AnyOut::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceAnysTest, DoubleRecursiveAny) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+ any->set_type_url("type.googleapis.com/google.protobuf.Any");
+
+ ::google::protobuf::Any nested_any;
+ nested_any.set_type_url("type.googleapis.com/google.protobuf.Any");
+
+ ::google::protobuf::Any second_nested_any;
+ second_nested_any.set_type_url(
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM");
+
+ AnyM m;
+ m.set_foo("foovalue");
+ second_nested_any.set_value(m.SerializeAsString());
+ nested_any.set_value(second_nested_any.SerializeAsString());
+ any->set_value(nested_any.SerializeAsString());
+
+ ow_.StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
+ ->StartObject("value")
+ ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
+ ->StartObject("value")
+ ->RenderString("@type",
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM")
+ ->RenderString("foo", "foovalue")
+ ->EndObject()
+ ->EndObject()
+ ->EndObject()
+ ->EndObject();
+
+ DoTest(out, AnyOut::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceAnysTest, EmptyAnyOutputsEmptyObject) {
+ AnyOut out;
+ out.mutable_any();
+
+ ow_.StartObject("")->StartObject("any")->EndObject()->EndObject();
+
+ DoTest(out, AnyOut::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceAnysTest, EmptyWithTypeAndNoValueOutputsType) {
+ AnyOut out;
+ out.mutable_any()->set_type_url("foo.googleapis.com/my.Type");
+
+ ow_.StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "foo.googleapis.com/my.Type")
+ ->EndObject()
+ ->EndObject();
+
+ DoTest(out, AnyOut::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceAnysTest, MissingTypeUrlError) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+
+ AnyM m;
+ m.set_foo("foovalue");
+ any->set_value(m.SerializeAsString());
+
+ // We start the "AnyOut" part and then fail when we hit the Any object.
+ ow_.StartObject("");
+
+ Status status = ExecuteTest(out, AnyOut::descriptor());
+ EXPECT_EQ(util::error::INTERNAL, status.error_code());
+}
+
+TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeServiceError) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+ any->set_type_url("foo.googleapis.com/my.own.Type");
+
+ AnyM m;
+ m.set_foo("foovalue");
+ any->set_value(m.SerializeAsString());
+
+ // We start the "AnyOut" part and then fail when we hit the Any object.
+ ow_.StartObject("");
+
+ Status status = ExecuteTest(out, AnyOut::descriptor());
+ EXPECT_EQ(util::error::INTERNAL, status.error_code());
+}
+
+TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeError) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+ any->set_type_url("type.googleapis.com/unknown.Type");
+
+ AnyM m;
+ m.set_foo("foovalue");
+ any->set_value(m.SerializeAsString());
+
+ // We start the "AnyOut" part and then fail when we hit the Any object.
+ ow_.StartObject("");
+
+ Status status = ExecuteTest(out, AnyOut::descriptor());
+ EXPECT_EQ(util::error::INTERNAL, status.error_code());
+}
+
+class ProtostreamObjectSourceStructTest : public ProtostreamObjectSourceTest {
+ protected:
+ ProtostreamObjectSourceStructTest() {
+ helper_.ResetTypeInfo(StructType::descriptor(),
+ google::protobuf::Struct::descriptor());
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
+ ProtostreamObjectSourceStructTest,
+ ::testing::Values(
+ testing::USE_TYPE_RESOLVER));
+
+// Tests struct
+//
+// "object": {
+// "k1": 123,
+// "k2": true
+// }
+TEST_P(ProtostreamObjectSourceStructTest, StructRenderSuccess) {
+ StructType out;
+ google::protobuf::Struct* s = out.mutable_object();
+ s->mutable_fields()->operator[]("k1").set_number_value(123);
+ s->mutable_fields()->operator[]("k2").set_bool_value(true);
+
+ ow_.StartObject("")
+ ->StartObject("object")
+ ->RenderDouble("k1", 123)
+ ->RenderBool("k2", true)
+ ->EndObject()
+ ->EndObject();
+
+ DoTest(out, StructType::descriptor());
+}
+
+TEST_P(ProtostreamObjectSourceStructTest, MissingValueSkipsField) {
+ StructType out;
+ google::protobuf::Struct* s = out.mutable_object();
+ s->mutable_fields()->operator[]("k1");
+
+ ow_.StartObject("")->StartObject("object")->EndObject()->EndObject();
+
+ DoTest(out, StructType::descriptor());
+}
+
+class ProtostreamObjectSourceFieldMaskTest
+ : public ProtostreamObjectSourceTest {
+ protected:
+ ProtostreamObjectSourceFieldMaskTest() {
+ helper_.ResetTypeInfo(FieldMaskTest::descriptor(),
+ google::protobuf::FieldMask::descriptor());
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
+ ProtostreamObjectSourceFieldMaskTest,
+ ::testing::Values(
+ testing::USE_TYPE_RESOLVER));
+
+TEST_P(ProtostreamObjectSourceFieldMaskTest, FieldMaskRenderSuccess) {
+ FieldMaskTest out;
+ out.set_id("1");
+ out.mutable_single_mask()->add_paths("path1");
+ out.mutable_single_mask()->add_paths("snake_case_path2");
+ ::google::protobuf::FieldMask* mask = out.add_repeated_mask();
+ mask->add_paths("path3");
+ mask = out.add_repeated_mask();
+ mask->add_paths("snake_case_path4");
+ mask->add_paths("path5");
+ NestedFieldMask* nested = out.add_nested_mask();
+ nested->set_data("data");
+ nested->mutable_single_mask()->add_paths("nested.path1");
+ nested->mutable_single_mask()->add_paths("nested_field.snake_case_path2");
+ mask = nested->add_repeated_mask();
+ mask->add_paths("nested_field.path3");
+ mask->add_paths("nested.snake_case_path4");
+ mask = nested->add_repeated_mask();
+ mask->add_paths("nested.path5");
+ mask = nested->add_repeated_mask();
+ mask->add_paths(
+ "snake_case.map_field[\"map_key_should_be_ignored\"].nested_snake_case."
+ "map_field[\"map_key_sho\\\"uld_be_ignored\"]");
+
+ ow_.StartObject("")
+ ->RenderString("id", "1")
+ ->RenderString("single_mask", "path1,snakeCasePath2")
+ ->StartList("repeated_mask")
+ ->RenderString("", "path3")
+ ->RenderString("", "snakeCasePath4,path5")
+ ->EndList()
+ ->StartList("nested_mask")
+ ->StartObject("")
+ ->RenderString("data", "data")
+ ->RenderString("single_mask", "nested.path1,nestedField.snakeCasePath2")
+ ->StartList("repeated_mask")
+ ->RenderString("", "nestedField.path3,nested.snakeCasePath4")
+ ->RenderString("", "nested.path5")
+ ->RenderString("",
+ "snakeCase.mapField[\"map_key_should_be_ignored\"]."
+ "nestedSnakeCase.mapField[\"map_key_sho\\\"uld_be_"
+ "ignored\"]")
+ ->EndList()
+ ->EndObject()
+ ->EndList()
+ ->EndObject();
+
+ DoTest(out, FieldMaskTest::descriptor());
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc
new file mode 100644
index 00000000..f9ddbf32
--- /dev/null
+++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc
@@ -0,0 +1,1557 @@
+// 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 <google/protobuf/util/internal/protostream_objectwriter.h>
+
+#include <functional>
+#include <stack>
+
+#include <google/protobuf/stubs/time.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/util/internal/field_mask_utility.h>
+#include <google/protobuf/util/internal/object_location_tracker.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/utility.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/statusor.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using google::protobuf::internal::WireFormatLite;
+using google::protobuf::io::CodedOutputStream;
+using util::error::INVALID_ARGUMENT;
+using util::Status;
+using util::StatusOr;
+
+
+ProtoStreamObjectWriter::ProtoStreamObjectWriter(
+ TypeResolver* type_resolver, const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener)
+ : master_type_(type),
+ typeinfo_(TypeInfo::NewTypeInfo(type_resolver)),
+ own_typeinfo_(true),
+ done_(false),
+ element_(NULL),
+ size_insert_(),
+ output_(output),
+ buffer_(),
+ adapter_(&buffer_),
+ stream_(new CodedOutputStream(&adapter_)),
+ listener_(listener),
+ invalid_depth_(0),
+ tracker_(new ObjectLocationTracker()) {}
+
+ProtoStreamObjectWriter::ProtoStreamObjectWriter(
+ TypeInfo* typeinfo, const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener)
+ : master_type_(type),
+ typeinfo_(typeinfo),
+ own_typeinfo_(false),
+ done_(false),
+ element_(NULL),
+ size_insert_(),
+ output_(output),
+ buffer_(),
+ adapter_(&buffer_),
+ stream_(new CodedOutputStream(&adapter_)),
+ listener_(listener),
+ invalid_depth_(0),
+ tracker_(new ObjectLocationTracker()) {}
+
+ProtoStreamObjectWriter::~ProtoStreamObjectWriter() {
+ // Cleanup explicitly in order to avoid destructor stack overflow when input
+ // is deeply nested.
+ while (element_ != NULL) {
+ element_.reset(element_->pop());
+ }
+ if (own_typeinfo_) {
+ delete typeinfo_;
+ }
+}
+
+namespace {
+
+// Writes an INT32 field, including tag to the stream.
+inline Status WriteInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// writes an SFIXED32 field, including tag, to the stream.
+inline Status WriteSFixed32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// Writes an SINT32 field, including tag, to the stream.
+inline Status WriteSInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int32> i32 = data.ToInt32();
+ if (i32.ok()) {
+ WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream);
+ }
+ return i32.status();
+}
+
+// Writes a FIXED32 field, including tag, to the stream.
+inline Status WriteFixed32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint32> u32 = data.ToUint32();
+ if (u32.ok()) {
+ WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream);
+ }
+ return u32.status();
+}
+
+// Writes a UINT32 field, including tag, to the stream.
+inline Status WriteUInt32(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint32> u32 = data.ToUint32();
+ if (u32.ok()) {
+ WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream);
+ }
+ return u32.status();
+}
+
+// Writes an INT64 field, including tag, to the stream.
+inline Status WriteInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes an SFIXED64 field, including tag, to the stream.
+inline Status WriteSFixed64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes an SINT64 field, including tag, to the stream.
+inline Status WriteSInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<int64> i64 = data.ToInt64();
+ if (i64.ok()) {
+ WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream);
+ }
+ return i64.status();
+}
+
+// Writes a FIXED64 field, including tag, to the stream.
+inline Status WriteFixed64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint64> u64 = data.ToUint64();
+ if (u64.ok()) {
+ WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream);
+ }
+ return u64.status();
+}
+
+// Writes a UINT64 field, including tag, to the stream.
+inline Status WriteUInt64(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<uint64> u64 = data.ToUint64();
+ if (u64.ok()) {
+ WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream);
+ }
+ return u64.status();
+}
+
+// Writes a DOUBLE field, including tag, to the stream.
+inline Status WriteDouble(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<double> d = data.ToDouble();
+ if (d.ok()) {
+ WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream);
+ }
+ return d.status();
+}
+
+// Writes a FLOAT field, including tag, to the stream.
+inline Status WriteFloat(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<float> f = data.ToFloat();
+ if (f.ok()) {
+ WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream);
+ }
+ return f.status();
+}
+
+// Writes a BOOL field, including tag, to the stream.
+inline Status WriteBool(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<bool> b = data.ToBool();
+ if (b.ok()) {
+ WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream);
+ }
+ return b.status();
+}
+
+// Writes a BYTES field, including tag, to the stream.
+inline Status WriteBytes(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<string> c = data.ToBytes();
+ if (c.ok()) {
+ WireFormatLite::WriteBytes(field_number, c.ValueOrDie(), stream);
+ }
+ return c.status();
+}
+
+// Writes a STRING field, including tag, to the stream.
+inline Status WriteString(int field_number, const DataPiece& data,
+ CodedOutputStream* stream) {
+ StatusOr<string> s = data.ToString();
+ if (s.ok()) {
+ WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream);
+ }
+ return s.status();
+}
+
+// Writes an ENUM field, including tag, to the stream.
+inline Status WriteEnum(int field_number, const DataPiece& data,
+ const google::protobuf::Enum* enum_type,
+ CodedOutputStream* stream) {
+ StatusOr<int> e = data.ToEnum(enum_type);
+ if (e.ok()) {
+ WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream);
+ }
+ return e.status();
+}
+
+// Given a google::protobuf::Type, returns the set of all required fields.
+std::set<const google::protobuf::Field*> GetRequiredFields(
+ const google::protobuf::Type& type) {
+ std::set<const google::protobuf::Field*> required;
+ for (int i = 0; i < type.fields_size(); i++) {
+ const google::protobuf::Field& field = type.fields(i);
+ if (field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
+ required.insert(&field);
+ }
+ }
+ return required;
+}
+
+// Utility method to split a string representation of Timestamp or Duration and
+// return the parts.
+void SplitSecondsAndNanos(StringPiece input, StringPiece* seconds,
+ StringPiece* nanos) {
+ size_t idx = input.rfind('.');
+ if (idx != string::npos) {
+ *seconds = input.substr(0, idx);
+ *nanos = input.substr(idx + 1);
+ } else {
+ *seconds = input;
+ *nanos = StringPiece();
+ }
+}
+
+} // namespace
+
+ProtoStreamObjectWriter::AnyWriter::AnyWriter(ProtoStreamObjectWriter* parent)
+ : parent_(parent),
+ ow_(),
+ invalid_(false),
+ data_(),
+ output_(&data_),
+ depth_(0),
+ has_injected_value_message_(false) {}
+
+ProtoStreamObjectWriter::AnyWriter::~AnyWriter() {}
+
+void ProtoStreamObjectWriter::AnyWriter::StartObject(StringPiece name) {
+ ++depth_;
+ // If an object writer is absent, that means we have not called StartAny()
+ // before reaching here. This is an invalid state. StartAny() gets called
+ // whenever we see an "@type" being rendered (see AnyWriter::RenderDataPiece).
+ if (ow_ == NULL) {
+ // Make sure we are not already in an invalid state. This avoids making
+ // multiple unnecessary InvalidValue calls.
+ if (!invalid_) {
+ parent_->InvalidValue("Any",
+ StrCat("Missing or invalid @type for any field in ",
+ parent_->master_type_.name()));
+ invalid_ = true;
+ }
+ } else if (!has_injected_value_message_ || depth_ != 1 || name != "value") {
+ // We don't propagate to ow_ StartObject("value") calls for nested Anys or
+ // Struct at depth 1 as they are nested one level deep with an injected
+ // "value" field.
+ ow_->StartObject(name);
+ }
+}
+
+bool ProtoStreamObjectWriter::AnyWriter::EndObject() {
+ --depth_;
+ // As long as depth_ >= 0, we know we haven't reached the end of Any.
+ // Propagate these EndObject() calls to the contained ow_. If we are in a
+ // nested Any or Struct type, ignore the second to last EndObject call (depth_
+ // == -1)
+ if (ow_ != NULL && (!has_injected_value_message_ || depth_ >= 0)) {
+ ow_->EndObject();
+ }
+ // A negative depth_ implies that we have reached the end of Any
+ // object. Now we write out its contents.
+ if (depth_ < 0) {
+ WriteAny();
+ return false;
+ }
+ return true;
+}
+
+void ProtoStreamObjectWriter::AnyWriter::StartList(StringPiece name) {
+ ++depth_;
+ // We expect ow_ to be present as this call only makes sense inside an Any.
+ if (ow_ == NULL) {
+ if (!invalid_) {
+ parent_->InvalidValue("Any",
+ StrCat("Missing or invalid @type for any field in ",
+ parent_->master_type_.name()));
+ invalid_ = true;
+ }
+ } else {
+ ow_->StartList(name);
+ }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::EndList() {
+ --depth_;
+ if (depth_ < 0) {
+ GOOGLE_LOG(DFATAL) << "Mismatched EndList found, should not be possible";
+ depth_ = 0;
+ }
+ // We don't write an error on the close, only on the open
+ if (ow_ != NULL) {
+ ow_->EndList();
+ }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::RenderDataPiece(
+ StringPiece name, const DataPiece& value) {
+ // Start an Any only at depth_ 0. Other RenderDataPiece calls with "@type"
+ // should go to the contained ow_ as they indicate nested Anys.
+ if (depth_ == 0 && ow_ == NULL && name == "@type") {
+ StartAny(value);
+ } else if (ow_ == NULL) {
+ if (!invalid_) {
+ parent_->InvalidValue("Any",
+ StrCat("Missing or invalid @type for any field in ",
+ parent_->master_type_.name()));
+ invalid_ = true;
+ }
+ } else {
+ // Check to see if the data needs to be rendered with well-known-type
+ // renderer.
+ const TypeRenderer* type_renderer =
+ FindTypeRenderer(GetFullTypeWithUrl(ow_->master_type_.name()));
+ if (type_renderer) {
+ // TODO(rikka): Don't just ignore the util::Status object!
+ (*type_renderer)(ow_.get(), value);
+ } else {
+ ow_->RenderDataPiece(name, value);
+ }
+ }
+}
+
+void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) {
+ // Figure out the type url. This is a copy-paste from WriteString but we also
+ // need the value, so we can't just call through to that.
+ if (value.type() == DataPiece::TYPE_STRING) {
+ type_url_ = value.str().ToString();
+ } else {
+ StatusOr<string> s = value.ToString();
+ if (!s.ok()) {
+ parent_->InvalidValue("String", s.status().error_message());
+ invalid_ = true;
+ return;
+ }
+ type_url_ = s.ValueOrDie();
+ }
+ // Resolve the type url, and report an error if we failed to resolve it.
+ StatusOr<const google::protobuf::Type*> resolved_type =
+ parent_->typeinfo_->ResolveTypeUrl(type_url_);
+ if (!resolved_type.ok()) {
+ parent_->InvalidValue("Any", resolved_type.status().error_message());
+ invalid_ = true;
+ return;
+ }
+ // At this point, type is never null.
+ const google::protobuf::Type* type = resolved_type.ValueOrDie();
+
+ // If this is the case of an Any in an Any or Struct in an Any, we need to
+ // expect a StartObject call with "value" while we're at depth_ 0, which we
+ // should ignore (not propagate to our nested object writer). We also need to
+ // ignore the second-to-last EndObject call, and not propagate that either.
+ if (type->name() == kAnyType || type->name() == kStructType) {
+ has_injected_value_message_ = true;
+ }
+
+ // Create our object writer and initialize it with the first StartObject
+ // call.
+ ow_.reset(new ProtoStreamObjectWriter(parent_->typeinfo_, *type, &output_,
+ parent_->listener_));
+ ow_->StartObject("");
+}
+
+void ProtoStreamObjectWriter::AnyWriter::WriteAny() {
+ if (ow_ == NULL) {
+ // If we had no object writer, we never got any content, so just return
+ // immediately, which is equivalent to writing an empty Any.
+ return;
+ }
+ // Render the type_url and value fields directly to the stream.
+ // type_url has tag 1 and value has tag 2.
+ WireFormatLite::WriteString(1, type_url_, parent_->stream_.get());
+ if (!data_.empty()) {
+ WireFormatLite::WriteBytes(2, data_, parent_->stream_.get());
+ }
+}
+
+ProtoStreamObjectWriter::ProtoElement::ProtoElement(
+ TypeInfo* typeinfo, const google::protobuf::Type& type,
+ ProtoStreamObjectWriter* enclosing)
+ : BaseElement(NULL),
+ ow_(enclosing),
+ any_(),
+ field_(NULL),
+ typeinfo_(typeinfo),
+ type_(type),
+ required_fields_(GetRequiredFields(type)),
+ is_repeated_type_(false),
+ size_index_(-1),
+ array_index_(-1),
+ element_type_(GetElementType(type_)) {
+ if (element_type_ == ANY) {
+ any_.reset(new AnyWriter(ow_));
+ }
+}
+
+ProtoStreamObjectWriter::ProtoElement::ProtoElement(
+ ProtoStreamObjectWriter::ProtoElement* parent,
+ const google::protobuf::Field* field, const google::protobuf::Type& type,
+ ElementType element_type)
+ : BaseElement(parent),
+ ow_(this->parent()->ow_),
+ any_(),
+ field_(field),
+ typeinfo_(this->parent()->typeinfo_),
+ type_(type),
+ is_repeated_type_(element_type == ProtoElement::LIST ||
+ element_type == ProtoElement::STRUCT_LIST ||
+ element_type == ProtoElement::MAP ||
+ element_type == ProtoElement::STRUCT_MAP),
+ size_index_(!is_repeated_type_ &&
+ field->kind() ==
+ google::protobuf::Field_Kind_TYPE_MESSAGE
+ ? ow_->size_insert_.size()
+ : -1),
+ array_index_(is_repeated_type_ ? 0 : -1),
+ element_type_(element_type) {
+ if (!is_repeated_type_) {
+ if (field->cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
+ // Update array_index_ if it is an explicit list.
+ if (this->parent()->array_index_ >= 0) this->parent()->array_index_++;
+ } else {
+ this->parent()->RegisterField(field);
+ }
+ if (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ required_fields_ = GetRequiredFields(type_);
+ int start_pos = ow_->stream_->ByteCount();
+ // length of serialized message is the final buffer position minus
+ // starting buffer position, plus length adjustments for size fields
+ // of any nested messages. We start with -start_pos here, so we only
+ // need to add the final buffer position to it at the end.
+ SizeInfo info = {start_pos, -start_pos};
+ ow_->size_insert_.push_back(info);
+ }
+ }
+ if (element_type == ANY) {
+ any_.reset(new AnyWriter(ow_));
+ }
+}
+
+ProtoStreamObjectWriter::ProtoElement*
+ProtoStreamObjectWriter::ProtoElement::pop() {
+ // Calls the registered error listener for any required field(s) not yet
+ // seen.
+ for (set<const google::protobuf::Field*>::iterator it =
+ required_fields_.begin();
+ it != required_fields_.end(); ++it) {
+ ow_->MissingField((*it)->name());
+ }
+ // Computes the total number of proto bytes used by a message, also adjusts
+ // the size of all parent messages by the length of this size field.
+ // If size_index_ < 0, this is not a message, so no size field is added.
+ if (size_index_ >= 0) {
+ // Add the final buffer position to compute the total length of this
+ // serialized message. The stored value (before this addition) already
+ // contains the total length of the size fields of all nested messages
+ // minus the initial buffer position.
+ ow_->size_insert_[size_index_].size += ow_->stream_->ByteCount();
+ // Calculate the length required to serialize the size field of the
+ // message, and propagate this additional size information upward to
+ // all enclosing messages.
+ int size = ow_->size_insert_[size_index_].size;
+ int length = CodedOutputStream::VarintSize32(size);
+ for (ProtoElement* e = parent(); e != NULL; e = e->parent()) {
+ // Only nested messages have size field, lists do not have size field.
+ if (e->size_index_ >= 0) {
+ ow_->size_insert_[e->size_index_].size += length;
+ }
+ }
+ }
+ return BaseElement::pop<ProtoElement>();
+}
+
+void ProtoStreamObjectWriter::ProtoElement::RegisterField(
+ const google::protobuf::Field* field) {
+ if (!required_fields_.empty() &&
+ field->cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REQUIRED) {
+ required_fields_.erase(field);
+ }
+}
+
+string ProtoStreamObjectWriter::ProtoElement::ToString() const {
+ if (parent() == NULL) return "";
+ string loc = parent()->ToString();
+ if (field_->cardinality() !=
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED ||
+ parent()->field_ != field_) {
+ string name = field_->name();
+ int i = 0;
+ while (i < name.size() && (ascii_isalnum(name[i]) || name[i] == '_')) ++i;
+ if (i > 0 && i == name.size()) { // safe field name
+ if (loc.empty()) {
+ loc = name;
+ } else {
+ StrAppend(&loc, ".", name);
+ }
+ } else {
+ StrAppend(&loc, "[\"", CEscape(name), "\"]");
+ }
+ }
+ if (field_->cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED &&
+ array_index_ > 0) {
+ StrAppend(&loc, "[", array_index_ - 1, "]");
+ }
+ return loc.empty() ? "." : loc;
+}
+
+inline void ProtoStreamObjectWriter::InvalidName(StringPiece unknown_name,
+ StringPiece message) {
+ listener_->InvalidName(location(), ToSnakeCase(unknown_name), message);
+}
+
+inline void ProtoStreamObjectWriter::InvalidValue(StringPiece type_name,
+ StringPiece value) {
+ listener_->InvalidValue(location(), type_name, value);
+}
+
+inline void ProtoStreamObjectWriter::MissingField(StringPiece missing_name) {
+ listener_->MissingField(location(), missing_name);
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject(
+ StringPiece name) {
+ // Starting the root message. Create the root ProtoElement and return.
+ if (element_ == NULL) {
+ if (!name.empty()) {
+ InvalidName(name, "Root element should not be named.");
+ }
+ element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+
+ // If master type is a special type that needs extra values to be written to
+ // stream, we write those values.
+ if (master_type_.name() == kStructType) {
+ StartStruct(NULL);
+ } else if (master_type_.name() == kStructValueType) {
+ // We got a StartObject call with google.protobuf.Value field. This means
+ // we are starting an object within google.protobuf.Value type. The only
+ // object within that type is a struct type. So start a struct.
+ const google::protobuf::Field* field = StartStructValueInStruct(NULL);
+ StartStruct(field);
+ }
+ return this;
+ }
+
+ const google::protobuf::Field* field = NULL;
+ if (element_ != NULL && element_->IsAny()) {
+ element_->any()->StartObject(name);
+ return this;
+ } else if (element_ != NULL &&
+ (element_->IsMap() || element_->IsStructMap())) {
+ field = StartMapEntry(name);
+ if (element_->IsStructMapEntry()) {
+ // If the top element is a map entry, this means we are starting another
+ // struct within a struct.
+ field = StartStructValueInStruct(field);
+ }
+ } else if (element_ != NULL && element_->IsStructList()) {
+ // If the top element is a list, then we are starting a list field within a
+ // struct.
+ field = Lookup(name);
+ field = StartStructValueInStruct(field);
+ } else {
+ field = BeginNamed(name, false);
+ }
+ if (field == NULL) {
+ return this;
+ }
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ ++invalid_depth_;
+ InvalidName(name,
+ StrCat("Missing descriptor for field: ", field->type_url()));
+ return this;
+ }
+
+ if (field->type_url() == GetFullTypeWithUrl(kStructType)) {
+ // Start a struct object.
+ StartStruct(field);
+ } else if (field->type_url() == GetFullTypeWithUrl(kStructValueType)) {
+ // We got a StartObject call with google.protobuf.Value field. This means we
+ // are starting an object within google.protobuf.Value type. The only object
+ // within that type is a struct type. So start a struct.
+ field = StartStructValueInStruct(field);
+ StartStruct(field);
+ } else if (field->type_url() == GetFullTypeWithUrl(kAnyType)) {
+ // Begin an Any. We can't do the real work till we get the @type field.
+ WriteTag(*field);
+ element_.reset(
+ new ProtoElement(element_.release(), field, *type, ProtoElement::ANY));
+ } else if (IsMap(*field)) {
+ // Begin a map.
+ // A map is triggered by a StartObject() call if the current field has a map
+ // type. Map values are written to proto in a manner detailed in comments
+ // above StartMapEntry() function.
+ element_.reset(
+ new ProtoElement(element_.release(), field, *type, ProtoElement::MAP));
+ } else {
+ WriteTag(*field);
+ element_.reset(new ProtoElement(element_.release(), field, *type,
+ ProtoElement::MESSAGE));
+ }
+ return this;
+}
+
+// Proto3 maps are represented on the wire as a message with
+// "key" and a "value".
+//
+// For example, the syntax:
+// map<key_type, value_type> map_field = N;
+//
+// is represented as:
+// message MapFieldEntry {
+// option map_entry = true; // marks the map construct in the descriptor
+//
+// key_type key = 1;
+// value_type value = 2;
+// }
+// repeated MapFieldEntry map_field = N;
+//
+// See go/proto3-maps for more information.
+const google::protobuf::Field* ProtoStreamObjectWriter::StartMapEntry(
+ StringPiece name) {
+ // top of stack is already a map field
+ const google::protobuf::Field* field = element_->field();
+ const google::protobuf::Type& type = element_->type();
+ // If we come from a regular map, use MAP_ENTRY or if we come from a struct,
+ // use STRUCT_MAP_ENTRY. These values are used later in StartObject/StartList
+ // or RenderDataPiece for making appropriate decisions.
+ ProtoElement::ElementType element_type = element_->IsStructMap()
+ ? ProtoElement::STRUCT_MAP_ENTRY
+ : ProtoElement::MAP_ENTRY;
+ WriteTag(*field);
+ element_.reset(
+ new ProtoElement(element_.release(), field, type, element_type));
+ RenderDataPiece("key", DataPiece(name));
+ return BeginNamed("value", false);
+}
+
+// Starts a google.protobuf.Struct.
+// 'field' represents a field in a message of type google.protobuf.Struct. A
+// struct contains a map with name 'fields'. This function starts this map as
+// well.
+// When 'field' is NULL, it means that the top level message is of struct
+// type.
+void ProtoStreamObjectWriter::StartStruct(
+ const google::protobuf::Field* field) {
+ const google::protobuf::Type* type = NULL;
+ if (field) {
+ type = LookupType(field);
+ WriteTag(*field);
+ element_.reset(new ProtoElement(element_.release(), field, *type,
+ ProtoElement::STRUCT));
+ }
+ const google::protobuf::Field* struct_field = BeginNamed("fields", false);
+
+ if (!struct_field) {
+ // It is a programmatic error if this happens. Log an error.
+ GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'fields' within "
+ << (field ? field->type_url() : "google.protobuf.Struct");
+ return;
+ }
+
+ type = LookupType(struct_field);
+ element_.reset(new ProtoElement(element_.release(), struct_field, *type,
+ ProtoElement::STRUCT_MAP));
+}
+
+// Starts a "struct_value" within struct.proto's google.protobuf.Value type.
+// 'field' should be of the type google.protobuf.Value.
+// Returns the field identifying "struct_value" within the given field.
+//
+// If field is NULL, then we are starting struct_value at the top-level, in
+// this case skip writing any tag information for the passed field.
+const google::protobuf::Field*
+ProtoStreamObjectWriter::StartStructValueInStruct(
+ const google::protobuf::Field* field) {
+ if (field) {
+ const google::protobuf::Type* type = LookupType(field);
+ WriteTag(*field);
+ element_.reset(new ProtoElement(element_.release(), field, *type,
+ ProtoElement::STRUCT_VALUE));
+ }
+ return BeginNamed("struct_value", false);
+}
+
+// Starts a "list_value" within struct.proto's google.protobuf.Value type.
+// 'field' should be of the type google.protobuf.Value.
+// Returns the field identifying "list_value" within the given field.
+//
+// If field is NULL, then we are starting list_value at the top-level, in
+// this case skip writing any tag information for the passed field.
+const google::protobuf::Field* ProtoStreamObjectWriter::StartListValueInStruct(
+ const google::protobuf::Field* field) {
+ if (field) {
+ const google::protobuf::Type* type = LookupType(field);
+ WriteTag(*field);
+ element_.reset(new ProtoElement(element_.release(), field, *type,
+ ProtoElement::STRUCT_VALUE));
+ }
+ const google::protobuf::Field* list_value = BeginNamed("list_value", false);
+
+ if (!list_value) {
+ // It is a programmatic error if this happens. Log an error.
+ GOOGLE_LOG(ERROR) << "Invalid internal state. Cannot find 'list_value' within "
+ << (field ? field->type_url() : "google.protobuf.Value");
+ return field;
+ }
+
+ return StartRepeatedValuesInListValue(list_value);
+}
+
+// Starts the repeated "values" field in struct.proto's
+// google.protobuf.ListValue type. 'field' should be of type
+// google.protobuf.ListValue.
+//
+// If field is NULL, then we are starting ListValue at the top-level, in
+// this case skip writing any tag information for the passed field.
+const google::protobuf::Field*
+ProtoStreamObjectWriter::StartRepeatedValuesInListValue(
+ const google::protobuf::Field* field) {
+ if (field) {
+ const google::protobuf::Type* type = LookupType(field);
+ WriteTag(*field);
+ element_.reset(new ProtoElement(element_.release(), field, *type,
+ ProtoElement::STRUCT_LIST_VALUE));
+ }
+ return BeginNamed("values", true);
+}
+
+void ProtoStreamObjectWriter::SkipElements() {
+ if (element_ == NULL) return;
+
+ ProtoElement::ElementType element_type = element_->element_type();
+ while (element_type == ProtoElement::STRUCT ||
+ element_type == ProtoElement::STRUCT_LIST_VALUE ||
+ element_type == ProtoElement::STRUCT_VALUE ||
+ element_type == ProtoElement::STRUCT_MAP_ENTRY ||
+ element_type == ProtoElement::MAP_ENTRY) {
+ element_.reset(element_->pop());
+ element_type =
+ element_ != NULL ? element_->element_type() : ProtoElement::MESSAGE;
+ }
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndObject() {
+ if (invalid_depth_ > 0) {
+ --invalid_depth_;
+ return this;
+ }
+ if (element_ != NULL && element_->IsAny()) {
+ if (element_->any()->EndObject()) {
+ return this;
+ }
+ }
+ if (element_ != NULL) {
+ element_.reset(element_->pop());
+ }
+
+ // Skip sentinel elements added to keep track of new proto3 types - map,
+ // struct.
+ SkipElements();
+
+ // If ending the root element,
+ // then serialize the full message with calculated sizes.
+ if (element_ == NULL) {
+ WriteRootMessage();
+ }
+ return this;
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) {
+ const google::protobuf::Field* field = NULL;
+ // Since we cannot have a top-level repeated item in protobuf, the only way
+ // element_ can be null when here is when we start a top-level list
+ // google.protobuf.ListValue.
+ if (element_ == NULL) {
+ if (!name.empty()) {
+ InvalidName(name, "Root element should not be named.");
+ }
+ element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+
+ // If master type is a special type that needs extra values to be written to
+ // stream, we write those values.
+ if (master_type_.name() == kStructValueType) {
+ // We got a StartList with google.protobuf.Value master type. This means
+ // we have to start the "list_value" within google.protobuf.Value.
+ field = StartListValueInStruct(NULL);
+ } else if (master_type_.name() == kStructListValueType) {
+ // We got a StartList with google.protobuf.ListValue master type. This
+ // means we have to start the "values" within google.protobuf.ListValue.
+ field = StartRepeatedValuesInListValue(NULL);
+ }
+
+ // field is NULL when master_type_ is anything other than
+ // google.protobuf.Value or google.protobuf.ListValue.
+ if (field) {
+ const google::protobuf::Type* type = LookupType(field);
+ element_.reset(new ProtoElement(element_.release(), field, *type,
+ ProtoElement::STRUCT_LIST));
+ }
+ return this;
+ }
+
+ if (element_->IsAny()) {
+ element_->any()->StartList(name);
+ return this;
+ }
+ // The type of element we push to stack.
+ ProtoElement::ElementType element_type = ProtoElement::LIST;
+
+ // Check if we need to start a map. This can heppen when there is either a map
+ // or a struct type within a list.
+ if (element_->IsMap() || element_->IsStructMap()) {
+ field = StartMapEntry(name);
+ if (field == NULL) return this;
+
+ if (element_->IsStructMapEntry()) {
+ // If the top element is a map entry, this means we are starting a list
+ // within a struct or a map.
+ // An example sequence of calls would be
+ // StartObject -> StartList
+ field = StartListValueInStruct(field);
+ if (field == NULL) return this;
+ }
+
+ element_type = ProtoElement::STRUCT_LIST;
+ } else if (element_->IsStructList()) {
+ // If the top element is a STRUCT_LIST, this means we are starting a list
+ // within the current list (inside a struct).
+ // An example call sequence would be
+ // StartObject -> StartList -> StartList
+ // with StartObject starting a struct.
+
+ // Lookup the last list type in element stack as we are adding an element of
+ // the same type.
+ field = Lookup(name);
+ if (field == NULL) return this;
+
+ field = StartListValueInStruct(field);
+ if (field == NULL) return this;
+
+ element_type = ProtoElement::STRUCT_LIST;
+ } else {
+ // Lookup field corresponding to 'name'. If it is a google.protobuf.Value
+ // or google.protobuf.ListValue type, then StartList is a valid call, start
+ // this list.
+ // We cannot use Lookup() here as it will produce InvalidName() error if the
+ // field is not found. We do not want to error here as it would cause us to
+ // report errors twice, once here and again later in BeginNamed() call.
+ // Also we ignore if the field is not found here as it is caught later.
+ field = typeinfo_->FindField(&element_->type(), name);
+
+ // It is an error to try to bind to map, which behind the scenes is a list.
+ if (field && IsMap(*field)) {
+ // Push field to stack for error location tracking & reporting.
+ element_.reset(new ProtoElement(element_.release(), field,
+ *LookupType(field),
+ ProtoElement::MESSAGE));
+ InvalidValue("Map", "Cannot bind a list to map.");
+ ++invalid_depth_;
+ element_->pop();
+ return this;
+ }
+
+ if (field && field->type_url() == GetFullTypeWithUrl(kStructValueType)) {
+ // There are 2 cases possible:
+ // a. g.p.Value is repeated
+ // b. g.p.Value is not repeated
+ //
+ // For case (a), the StartList should bind to the repeated g.p.Value.
+ // For case (b), the StartList should bind to g.p.ListValue within the
+ // g.p.Value.
+ //
+ // This means, for case (a), we treat it just like any other repeated
+ // message, except we would apply an appropriate element_type so future
+ // Start or Render calls are routed appropriately.
+ if (field->cardinality() !=
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
+ field = StartListValueInStruct(field);
+ }
+ element_type = ProtoElement::STRUCT_LIST;
+ } else if (field &&
+ field->type_url() == GetFullTypeWithUrl(kStructListValueType)) {
+ // We got a StartList with google.protobuf.ListValue master type. This
+ // means we have to start the "values" within google.protobuf.ListValue.
+ field = StartRepeatedValuesInListValue(field);
+ } else {
+ // If no special types are to be bound, fall back to normal processing of
+ // StartList.
+ field = BeginNamed(name, true);
+ }
+ if (field == NULL) return this;
+ }
+
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ ++invalid_depth_;
+ InvalidName(name,
+ StrCat("Missing descriptor for field: ", field->type_url()));
+ return this;
+ }
+
+ element_.reset(
+ new ProtoElement(element_.release(), field, *type, element_type));
+ return this;
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::EndList() {
+ if (invalid_depth_ > 0) {
+ --invalid_depth_;
+ } else if (element_ != NULL) {
+ if (element_->IsAny()) {
+ element_->any()->EndList();
+ } else {
+ element_.reset(element_->pop());
+ // Skip sentinel elements added to keep track of new proto3 types - map,
+ // struct.
+ SkipElements();
+ }
+ }
+
+ // When element_ is NULL, we have reached the root message type. Write out
+ // the bytes.
+ if (element_ == NULL) {
+ WriteRootMessage();
+ }
+ return this;
+}
+
+Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow,
+ const DataPiece& data) {
+ string struct_field_name;
+ switch (data.type()) {
+ // Our JSON parser parses numbers as either int64, uint64, or double.
+ case DataPiece::TYPE_INT64:
+ case DataPiece::TYPE_UINT64:
+ case DataPiece::TYPE_DOUBLE: {
+ struct_field_name = "number_value";
+ break;
+ }
+ case DataPiece::TYPE_STRING: {
+ struct_field_name = "string_value";
+ break;
+ }
+ case DataPiece::TYPE_BOOL: {
+ struct_field_name = "bool_value";
+ break;
+ }
+ case DataPiece::TYPE_NULL: {
+ struct_field_name = "null_value";
+ break;
+ }
+ default: {
+ return Status(INVALID_ARGUMENT,
+ "Invalid struct data type. Only number, string, boolean or "
+ "null values are supported.");
+ }
+ }
+ ow->RenderDataPiece(struct_field_name, data);
+ return Status::OK;
+}
+
+Status ProtoStreamObjectWriter::RenderTimestamp(ProtoStreamObjectWriter* ow,
+ const DataPiece& data) {
+ if (data.type() != DataPiece::TYPE_STRING) {
+ return Status(INVALID_ARGUMENT,
+ StrCat("Invalid data type for timestamp, value is ",
+ data.ValueAsStringOrDefault("")));
+ }
+
+ StringPiece value(data.str());
+
+ int64 seconds;
+ int32 nanos;
+ if (!::google::protobuf::internal::ParseTime(value.ToString(), &seconds,
+ &nanos)) {
+ return Status(INVALID_ARGUMENT, StrCat("Invalid time format: ", value));
+ }
+
+
+ ow->RenderDataPiece("seconds", DataPiece(seconds));
+ ow->RenderDataPiece("nanos", DataPiece(nanos));
+ return Status::OK;
+}
+
+static inline util::Status RenderOneFieldPath(ProtoStreamObjectWriter* ow,
+ StringPiece path) {
+ ow->RenderDataPiece("paths",
+ DataPiece(ConvertFieldMaskPath(path, &ToSnakeCase)));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow,
+ const DataPiece& data) {
+ if (data.type() != DataPiece::TYPE_STRING) {
+ return Status(INVALID_ARGUMENT,
+ StrCat("Invalid data type for field mask, value is ",
+ data.ValueAsStringOrDefault("")));
+ }
+
+ // TODO(tsun): figure out how to do proto descriptor based snake case
+ // conversions as much as possible. Because ToSnakeCase sometimes returns the
+ // wrong value.
+ google::protobuf::scoped_ptr<ResultCallback1<util::Status, StringPiece> > callback(
+ NewPermanentCallback(&RenderOneFieldPath, ow));
+ return DecodeCompactFieldMaskPaths(data.str(), callback.get());
+}
+
+Status ProtoStreamObjectWriter::RenderDuration(ProtoStreamObjectWriter* ow,
+ const DataPiece& data) {
+ if (data.type() != DataPiece::TYPE_STRING) {
+ return Status(INVALID_ARGUMENT,
+ StrCat("Invalid data type for duration, value is ",
+ data.ValueAsStringOrDefault("")));
+ }
+
+ StringPiece value(data.str());
+
+ if (!value.ends_with("s")) {
+ return Status(INVALID_ARGUMENT,
+ "Illegal duration format; duration must end with 's'");
+ }
+ value = value.substr(0, value.size() - 1);
+ int sign = 1;
+ if (value.starts_with("-")) {
+ sign = -1;
+ value = value.substr(1);
+ }
+
+ StringPiece s_secs, s_nanos;
+ SplitSecondsAndNanos(value, &s_secs, &s_nanos);
+ uint64 unsigned_seconds;
+ if (!safe_strtou64(s_secs, &unsigned_seconds)) {
+ return Status(INVALID_ARGUMENT,
+ "Invalid duration format, failed to parse seconds");
+ }
+
+ double d_nanos = 0;
+ if (!safe_strtod("0." + s_nanos.ToString(), &d_nanos)) {
+ return Status(INVALID_ARGUMENT,
+ "Invalid duration format, failed to parse nanos seconds");
+ }
+
+ int32 nanos = sign * static_cast<int32>(d_nanos * kNanosPerSecond);
+ int64 seconds = sign * unsigned_seconds;
+
+ if (seconds > kMaxSeconds || seconds < kMinSeconds ||
+ nanos <= -kNanosPerSecond || nanos >= kNanosPerSecond) {
+ return Status(INVALID_ARGUMENT, "Duration value exceeds limits");
+ }
+
+ ow->RenderDataPiece("seconds", DataPiece(seconds));
+ ow->RenderDataPiece("nanos", DataPiece(nanos));
+ return Status::OK;
+}
+
+Status ProtoStreamObjectWriter::RenderWrapperType(ProtoStreamObjectWriter* ow,
+ const DataPiece& data) {
+ ow->RenderDataPiece("value", data);
+ return Status::OK;
+}
+
+ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece(
+ StringPiece name, const DataPiece& data) {
+ Status status;
+ if (invalid_depth_ > 0) return this;
+ if (element_ != NULL && element_->IsAny()) {
+ element_->any()->RenderDataPiece(name, data);
+ return this;
+ }
+
+ const google::protobuf::Field* field = NULL;
+ string type_url;
+ bool is_map_entry = false;
+ if (element_ == NULL) {
+ type_url = GetFullTypeWithUrl(master_type_.name());
+ } else {
+ if (element_->IsMap() || element_->IsStructMap()) {
+ is_map_entry = true;
+ field = StartMapEntry(name);
+ } else {
+ field = Lookup(name);
+ }
+ if (field == NULL) {
+ return this;
+ }
+ type_url = field->type_url();
+ }
+
+ // Check if there are any well known type renderers available for type_url.
+ const TypeRenderer* type_renderer = FindTypeRenderer(type_url);
+ if (type_renderer != NULL) {
+ // Push the current element to stack so lookups in type_renderer will
+ // find the fields. We do an EndObject soon after, which pops this. This is
+ // safe because all well-known types are messages.
+ if (element_ == NULL) {
+ element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+ } else {
+ if (field) {
+ WriteTag(*field);
+ const google::protobuf::Type* type = LookupType(field);
+ element_.reset(new ProtoElement(element_.release(), field, *type,
+ ProtoElement::MESSAGE));
+ }
+ }
+ status = (*type_renderer)(this, data);
+ if (!status.ok()) {
+ InvalidValue(type_url,
+ StrCat("Field '", name, "', ", status.error_message()));
+ }
+ EndObject();
+ return this;
+ } else if (element_ == NULL) { // no message type found at root
+ element_.reset(new ProtoElement(typeinfo_, master_type_, this));
+ InvalidName(name, "Root element must be a message.");
+ return this;
+ }
+
+ if (field == NULL) {
+ return this;
+ }
+ const google::protobuf::Type* type = LookupType(field);
+ if (type == NULL) {
+ InvalidName(name,
+ StrCat("Missing descriptor for field: ", field->type_url()));
+ return this;
+ }
+
+ // Whether we should pop at the end. Set to true if the data field is a
+ // message type, which can happen in case of struct values.
+ bool should_pop = false;
+
+ RenderSimpleDataPiece(*field, *type, data);
+
+ if (should_pop && element_ != NULL) {
+ element_.reset(element_->pop());
+ }
+
+ if (is_map_entry) {
+ // Ending map is the same as ending an object.
+ EndObject();
+ }
+ return this;
+}
+
+void ProtoStreamObjectWriter::RenderSimpleDataPiece(
+ const google::protobuf::Field& field, const google::protobuf::Type& type,
+ const DataPiece& data) {
+ // If we are rendering explicit null values and the backend proto field is not
+ // of the google.protobuf.NullType type, we do nothing.
+ if (data.type() == DataPiece::TYPE_NULL &&
+ field.type_url() != kStructNullValueTypeUrl) {
+ return;
+ }
+
+ // Pushing a ProtoElement and then pop it off at the end for 2 purposes:
+ // error location reporting and required field accounting.
+ element_.reset(new ProtoElement(element_.release(), &field, type,
+ ProtoElement::MESSAGE));
+
+ // Make sure that field represents a simple data type.
+ if (field.kind() == google::protobuf::Field_Kind_TYPE_UNKNOWN ||
+ field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) {
+ InvalidValue(field.type_url().empty()
+ ? google::protobuf::Field_Kind_Name(field.kind())
+ : field.type_url(),
+ data.ValueAsStringOrDefault(""));
+ return;
+ }
+
+ Status status;
+ switch (field.kind()) {
+ case google::protobuf::Field_Kind_TYPE_INT32: {
+ status = WriteInt32(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED32: {
+ status = WriteSFixed32(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT32: {
+ status = WriteSInt32(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED32: {
+ status = WriteFixed32(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT32: {
+ status = WriteUInt32(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_INT64: {
+ status = WriteInt64(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SFIXED64: {
+ status = WriteSFixed64(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_SINT64: {
+ status = WriteSInt64(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FIXED64: {
+ status = WriteFixed64(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_UINT64: {
+ status = WriteUInt64(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_DOUBLE: {
+ status = WriteDouble(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_FLOAT: {
+ status = WriteFloat(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BOOL: {
+ status = WriteBool(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_BYTES: {
+ status = WriteBytes(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_STRING: {
+ status = WriteString(field.number(), data, stream_.get());
+ break;
+ }
+ case google::protobuf::Field_Kind_TYPE_ENUM: {
+ status = WriteEnum(field.number(), data,
+ typeinfo_->GetEnum(field.type_url()), stream_.get());
+ break;
+ }
+ default: // TYPE_GROUP or TYPE_MESSAGE
+ status = Status(INVALID_ARGUMENT, data.ToString().ValueOrDie());
+ }
+ if (!status.ok()) {
+ InvalidValue(google::protobuf::Field_Kind_Name(field.kind()),
+ status.error_message());
+ }
+ element_.reset(element_->pop());
+}
+
+// Map of functions that are responsible for rendering well known type
+// represented by the key.
+hash_map<string, ProtoStreamObjectWriter::TypeRenderer>*
+ProtoStreamObjectWriter::CreateRendererMap() {
+ google::protobuf::scoped_ptr<hash_map<string, ProtoStreamObjectWriter::TypeRenderer> >
+ result(new hash_map<string, ProtoStreamObjectWriter::TypeRenderer>());
+ (*result)["type.googleapis.com/google.protobuf.Timestamp"] =
+ &ProtoStreamObjectWriter::RenderTimestamp;
+ (*result)["type.googleapis.com/google.protobuf.Duration"] =
+ &ProtoStreamObjectWriter::RenderDuration;
+ (*result)["type.googleapis.com/google.protobuf.FieldMask"] =
+ &ProtoStreamObjectWriter::RenderFieldMask;
+ (*result)["type.googleapis.com/google.protobuf.Double"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Float"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Int64"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.UInt64"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Int32"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.UInt32"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Bool"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.String"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Bytes"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.DoubleValue"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.FloatValue"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Int64Value"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.UInt64Value"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Int32Value"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.UInt32Value"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.BoolValue"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.StringValue"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.BytesValue"] =
+ &ProtoStreamObjectWriter::RenderWrapperType;
+ (*result)["type.googleapis.com/google.protobuf.Value"] =
+ &ProtoStreamObjectWriter::RenderStructValue;
+ return result.release();
+}
+
+ProtoStreamObjectWriter::TypeRenderer*
+ProtoStreamObjectWriter::FindTypeRenderer(const string& type_url) {
+ static hash_map<string, TypeRenderer>* renderers = CreateRendererMap();
+ return FindOrNull(*renderers, type_url);
+}
+
+ProtoStreamObjectWriter::ProtoElement::ElementType
+ProtoStreamObjectWriter::GetElementType(const google::protobuf::Type& type) {
+ if (type.name() == kAnyType) {
+ return ProtoElement::ANY;
+ } else if (type.name() == kStructType) {
+ return ProtoElement::STRUCT;
+ } else if (type.name() == kStructValueType) {
+ return ProtoElement::STRUCT_VALUE;
+ } else if (type.name() == kStructListValueType) {
+ return ProtoElement::STRUCT_LIST_VALUE;
+ } else {
+ return ProtoElement::MESSAGE;
+ }
+}
+
+const google::protobuf::Field* ProtoStreamObjectWriter::BeginNamed(
+ StringPiece name, bool is_list) {
+ if (invalid_depth_ > 0) {
+ ++invalid_depth_;
+ return NULL;
+ }
+ const google::protobuf::Field* field = Lookup(name);
+ if (field == NULL) {
+ ++invalid_depth_;
+ // InvalidName() already called in Lookup().
+ return NULL;
+ }
+ if (is_list &&
+ field->cardinality() !=
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
+ ++invalid_depth_;
+ InvalidName(name, "Proto field is not repeating, cannot start list.");
+ return NULL;
+ }
+ return field;
+}
+
+const google::protobuf::Field* ProtoStreamObjectWriter::Lookup(
+ StringPiece unnormalized_name) {
+ ProtoElement* e = element();
+ if (e == NULL) {
+ InvalidName(unnormalized_name, "Root element must be a message.");
+ return NULL;
+ }
+ if (unnormalized_name.empty()) {
+ // Objects in repeated field inherit the same field descriptor.
+ if (e->field() == NULL) {
+ InvalidName(unnormalized_name, "Proto fields must have a name.");
+ } else if (e->field()->cardinality() !=
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
+ InvalidName(unnormalized_name, "Proto fields must have a name.");
+ return NULL;
+ }
+ return e->field();
+ }
+ const google::protobuf::Field* field =
+ typeinfo_->FindField(&e->type(), unnormalized_name);
+ if (field == NULL) InvalidName(unnormalized_name, "Cannot find field.");
+ return field;
+}
+
+const google::protobuf::Type* ProtoStreamObjectWriter::LookupType(
+ const google::protobuf::Field* field) {
+ return (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE
+ ? typeinfo_->GetType(field->type_url())
+ : &element_->type());
+}
+
+// Looks up the oneof struct field based on the data type.
+StatusOr<const google::protobuf::Field*>
+ProtoStreamObjectWriter::LookupStructField(DataPiece::Type type) {
+ const google::protobuf::Field* field = NULL;
+ switch (type) {
+ // Our JSON parser parses numbers as either int64, uint64, or double.
+ case DataPiece::TYPE_INT64:
+ case DataPiece::TYPE_UINT64:
+ case DataPiece::TYPE_DOUBLE: {
+ field = Lookup("number_value");
+ break;
+ }
+ case DataPiece::TYPE_STRING: {
+ field = Lookup("string_value");
+ break;
+ }
+ case DataPiece::TYPE_BOOL: {
+ field = Lookup("bool_value");
+ break;
+ }
+ case DataPiece::TYPE_NULL: {
+ field = Lookup("null_value");
+ break;
+ }
+ default: { return Status(INVALID_ARGUMENT, "Invalid struct data type"); }
+ }
+ if (field == NULL) {
+ return Status(INVALID_ARGUMENT, "Could not lookup struct field");
+ }
+ return field;
+}
+
+void ProtoStreamObjectWriter::WriteRootMessage() {
+ GOOGLE_DCHECK(!done_);
+ int curr_pos = 0;
+ // Calls the destructor of CodedOutputStream to remove any uninitialized
+ // memory from the Cord before we read it.
+ stream_.reset(NULL);
+ const void* data;
+ int length;
+ google::protobuf::io::ArrayInputStream input_stream(buffer_.data(), buffer_.size());
+ while (input_stream.Next(&data, &length)) {
+ if (length == 0) continue;
+ int num_bytes = length;
+ // Write up to where we need to insert the size field.
+ // The number of bytes we may write is the smaller of:
+ // - the current fragment size
+ // - the distance to the next position where a size field needs to be
+ // inserted.
+ if (!size_insert_.empty() &&
+ size_insert_.front().pos - curr_pos < num_bytes) {
+ num_bytes = size_insert_.front().pos - curr_pos;
+ }
+ output_->Append(static_cast<const char*>(data), num_bytes);
+ if (num_bytes < length) {
+ input_stream.BackUp(length - num_bytes);
+ }
+ curr_pos += num_bytes;
+ // Insert the size field.
+ // size_insert_.front(): the next <index, size> pair to be written.
+ // size_insert_.front().pos: position of the size field.
+ // size_insert_.front().size: the size (integer) to be inserted.
+ if (!size_insert_.empty() && curr_pos == size_insert_.front().pos) {
+ // Varint32 occupies at most 10 bytes.
+ uint8 insert_buffer[10];
+ uint8* insert_buffer_pos = CodedOutputStream::WriteVarint32ToArray(
+ size_insert_.front().size, insert_buffer);
+ output_->Append(reinterpret_cast<const char*>(insert_buffer),
+ insert_buffer_pos - insert_buffer);
+ size_insert_.pop_front();
+ }
+ }
+ output_->Flush();
+ stream_.reset(new CodedOutputStream(&adapter_));
+ done_ = true;
+}
+
+bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) {
+ if (field.type_url().empty() ||
+ field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE ||
+ field.cardinality() !=
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) {
+ return false;
+ }
+ const google::protobuf::Type* field_type =
+ typeinfo_->GetType(field.type_url());
+
+ return GetBoolOptionOrDefault(field_type->options(),
+ "google.protobuf.MessageOptions.map_entry", false);
+}
+
+void ProtoStreamObjectWriter::WriteTag(const google::protobuf::Field& field) {
+ WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType(
+ static_cast<WireFormatLite::FieldType>(field.kind()));
+ stream_->WriteTag(WireFormatLite::MakeTag(field.number(), wire_type));
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h
new file mode 100644
index 00000000..eb4a59f9
--- /dev/null
+++ b/src/google/protobuf/util/internal/protostream_objectwriter.h
@@ -0,0 +1,455 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__
+
+#include <deque>
+#include <google/protobuf/stubs/hash.h>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/internal/datapiece.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/structured_objectwriter.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/bytestream.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+class CodedOutputStream;
+} // namespace io
+} // namespace protobuf
+
+
+namespace protobuf {
+class Type;
+class Field;
+} // namespace protobuf
+
+
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class ObjectLocationTracker;
+
+// An ObjectWriter that can write protobuf bytes directly from writer events.
+//
+// It also supports streaming.
+class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter {
+ public:
+ // Constructor. Does not take ownership of any parameter passed in.
+ ProtoStreamObjectWriter(TypeResolver* type_resolver,
+ const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener);
+ virtual ~ProtoStreamObjectWriter();
+
+ // ObjectWriter methods.
+ virtual ProtoStreamObjectWriter* StartObject(StringPiece name);
+ virtual ProtoStreamObjectWriter* EndObject();
+ virtual ProtoStreamObjectWriter* StartList(StringPiece name);
+ virtual ProtoStreamObjectWriter* EndList();
+ virtual ProtoStreamObjectWriter* RenderBool(StringPiece name,
+ bool value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name,
+ int32 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name,
+ uint32 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name,
+ int64 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name,
+ uint64 value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderDouble(StringPiece name,
+ double value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name,
+ float value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderString(StringPiece name,
+ StringPiece value) {
+ return RenderDataPiece(name, DataPiece(value));
+ }
+ virtual ProtoStreamObjectWriter* RenderBytes(StringPiece name,
+ StringPiece value) {
+ return RenderDataPiece(name, DataPiece(value, false));
+ }
+ virtual ProtoStreamObjectWriter* RenderNull(StringPiece name) {
+ return RenderDataPiece(name, DataPiece::NullData());
+ }
+
+ // Renders a DataPiece 'value' into a field whose wire type is determined
+ // from the given field 'name'.
+ ProtoStreamObjectWriter* RenderDataPiece(StringPiece name,
+ const DataPiece& value);
+
+ // Returns the location tracker to use for tracking locations for errors.
+ const LocationTrackerInterface& location() {
+ return element_ != NULL ? *element_ : *tracker_;
+ }
+
+ // When true, we finished writing to output a complete message.
+ bool done() const { return done_; }
+
+ private:
+ // Function that renders a well known type with modified behavior.
+ typedef util::Status (*TypeRenderer)(ProtoStreamObjectWriter*,
+ const DataPiece&);
+
+ // Handles writing Anys out using nested object writers and the like.
+ class LIBPROTOBUF_EXPORT AnyWriter {
+ public:
+ explicit AnyWriter(ProtoStreamObjectWriter* parent);
+ ~AnyWriter();
+
+ // Passes a StartObject call through to the Any writer.
+ void StartObject(StringPiece name);
+
+ // Passes an EndObject call through to the Any. Returns true if the any
+ // handled the EndObject call, false if the Any is now all done and is no
+ // longer needed.
+ bool EndObject();
+
+ // Passes a StartList call through to the Any writer.
+ void StartList(StringPiece name);
+
+ // Passes an EndList call through to the Any writer.
+ void EndList();
+
+ // Renders a data piece on the any.
+ void RenderDataPiece(StringPiece name, const DataPiece& value);
+
+ private:
+ // Handles starting up the any once we have a type.
+ void StartAny(const DataPiece& value);
+
+ // Writes the Any out to the parent writer in its serialized form.
+ void WriteAny();
+
+ // The parent of this writer, needed for various bits such as type info and
+ // the listeners.
+ ProtoStreamObjectWriter* parent_;
+
+ // The nested object writer, used to write events.
+ google::protobuf::scoped_ptr<ProtoStreamObjectWriter> ow_;
+
+ // The type_url_ that this Any represents.
+ string type_url_;
+
+ // Whether this any is invalid. This allows us to only report an invalid
+ // Any message a single time rather than every time we get a nested field.
+ bool invalid_;
+
+ // The output data and wrapping ByteSink.
+ string data_;
+ strings::StringByteSink output_;
+
+ // The depth within the Any, so we can track when we're done.
+ int depth_;
+
+ // True if the message type contained in Any has a special "value" message
+ // injected. This is true for well-known message types like Any or Struct.
+ bool has_injected_value_message_;
+ };
+
+ class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface {
+ public:
+ // Indicates the type of element. Special types like LIST, MAP, MAP_ENTRY,
+ // STRUCT etc. are used to deduce other information based on their position
+ // on the stack of elements.
+ enum ElementType {
+ MESSAGE, // Simple message
+ LIST, // List/repeated element
+ MAP, // Proto3 map type
+ MAP_ENTRY, // Proto3 map message type, with 'key' and 'value' fields
+ ANY, // Proto3 Any type
+ STRUCT, // Proto3 struct type
+ STRUCT_VALUE, // Struct's Value message type
+ STRUCT_LIST, // List type indicator within a struct
+ STRUCT_LIST_VALUE, // Struct Value's ListValue message type
+ STRUCT_MAP, // Struct within a struct type
+ STRUCT_MAP_ENTRY // Struct map's entry type with 'key' and 'value'
+ // fields
+ };
+
+ // Constructor for the root element. No parent nor field.
+ ProtoElement(TypeInfo* typeinfo, const google::protobuf::Type& type,
+ ProtoStreamObjectWriter* enclosing);
+
+ // Constructor for a field of an element.
+ ProtoElement(ProtoElement* parent, const google::protobuf::Field* field,
+ const google::protobuf::Type& type, ElementType element_type);
+
+ virtual ~ProtoElement() {}
+
+ // Called just before the destructor for clean up:
+ // - reports any missing required fields
+ // - computes the space needed by the size field, and augment the
+ // length of all parent messages by this additional space.
+ // - releases and returns the parent pointer.
+ ProtoElement* pop();
+
+ // Accessors
+ const google::protobuf::Field* field() const { return field_; }
+ const google::protobuf::Type& type() const { return type_; }
+
+ // These functions return true if the element type is corresponding to the
+ // type in function name.
+ bool IsMap() { return element_type_ == MAP; }
+ bool IsStructMap() { return element_type_ == STRUCT_MAP; }
+ bool IsStructMapEntry() { return element_type_ == STRUCT_MAP_ENTRY; }
+ bool IsStructList() { return element_type_ == STRUCT_LIST; }
+ bool IsAny() { return element_type_ == ANY; }
+
+ ElementType element_type() { return element_type_; }
+
+ void RegisterField(const google::protobuf::Field* field);
+ virtual string ToString() const;
+
+ AnyWriter* any() const { return any_.get(); }
+
+ virtual ProtoElement* parent() const {
+ return static_cast<ProtoElement*>(BaseElement::parent());
+ }
+
+ private:
+ // Used for access to variables of the enclosing instance of
+ // ProtoStreamObjectWriter.
+ ProtoStreamObjectWriter* ow_;
+
+ // A writer for Any objects, handles all Any-related nonsense.
+ google::protobuf::scoped_ptr<AnyWriter> any_;
+
+ // Describes the element as a field in the parent message.
+ // field_ is NULL if and only if this element is the root element.
+ const google::protobuf::Field* field_;
+
+ // TypeInfo to lookup types.
+ TypeInfo* typeinfo_;
+
+ // Additional variables if this element is a message:
+ // (Root element is always a message).
+ // descriptor_ : describes allowed fields in the message.
+ // required_fields_: set of required fields.
+ // is_repeated_type_ : true if the element is of type list or map.
+ // size_index_ : index into ProtoStreamObjectWriter::size_insert_
+ // for later insertion of serialized message length.
+ const google::protobuf::Type& type_;
+ std::set<const google::protobuf::Field*> required_fields_;
+ const bool is_repeated_type_;
+ const int size_index_;
+
+ // Tracks position in repeated fields, needed for LocationTrackerInterface.
+ int array_index_;
+
+ // The type of this element, see enum for permissible types.
+ ElementType element_type_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement);
+ };
+
+ // Container for inserting 'size' information at the 'pos' position.
+ struct SizeInfo {
+ const int pos;
+ int size;
+ };
+
+ ProtoStreamObjectWriter(TypeInfo* typeinfo,
+ const google::protobuf::Type& type,
+ strings::ByteSink* output, ErrorListener* listener);
+
+ ProtoElement* element() { return element_.get(); }
+
+ // Helper methods for calling ErrorListener. See error_listener.h.
+ void InvalidName(StringPiece unknown_name, StringPiece message);
+ void InvalidValue(StringPiece type_name, StringPiece value);
+ void MissingField(StringPiece missing_name);
+
+ // Common code for BeginObject() and BeginList() that does invalid_depth_
+ // bookkeeping associated with name lookup.
+ const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list);
+
+ // Lookup the field in the current element. Looks in the base descriptor
+ // and in any extension. This will report an error if the field cannot be
+ // found or if multiple matching extensions are found.
+ const google::protobuf::Field* Lookup(StringPiece name);
+
+ // Lookup the field type in the type descriptor. Returns NULL if the type
+ // is not known.
+ const google::protobuf::Type* LookupType(
+ const google::protobuf::Field* field);
+
+ // Looks up the oneof struct Value field depending on the type.
+ // On failure to find, it returns an appropriate error.
+ util::StatusOr<const google::protobuf::Field*> LookupStructField(
+ DataPiece::Type type);
+
+ // Starts an entry in map. This will be called after placing map element at
+ // the top of the stack. Uses this information to write map entries.
+ const google::protobuf::Field* StartMapEntry(StringPiece name);
+
+ // Starts a google.protobuf.Struct.
+ // 'field' is of type google.protobuf.Struct.
+ // If field is NULL, it indicates that the top-level message is a struct
+ // type.
+ void StartStruct(const google::protobuf::Field* field);
+
+ // Starts another struct within a struct.
+ // 'field' is of type google.protobuf.Value (see struct.proto).
+ const google::protobuf::Field* StartStructValueInStruct(
+ const google::protobuf::Field* field);
+
+ // Starts a list within a struct.
+ // 'field' is of type google.protobuf.ListValue (see struct.proto).
+ const google::protobuf::Field* StartListValueInStruct(
+ const google::protobuf::Field* field);
+
+ // Starts the repeated "values" field in struct.proto's
+ // google.protobuf.ListValue type. 'field' should be of type
+ // google.protobuf.ListValue.
+ const google::protobuf::Field* StartRepeatedValuesInListValue(
+ const google::protobuf::Field* field);
+
+ // Pops sentinel elements off the stack.
+ void SkipElements();
+
+ // Write serialized output to the final output ByteSink, inserting all
+ // the size information for nested messages that are missing from the
+ // intermediate Cord buffer.
+ void WriteRootMessage();
+
+ // Returns true if the field is a map.
+ bool IsMap(const google::protobuf::Field& field);
+
+ // Returns true if the field is an any.
+ bool IsAny(const google::protobuf::Field& field);
+
+ // Helper method to write proto tags based on the given field.
+ void WriteTag(const google::protobuf::Field& field);
+
+ // Helper function to render primitive data types in DataPiece.
+ void RenderSimpleDataPiece(const google::protobuf::Field& field,
+ const google::protobuf::Type& type,
+ const DataPiece& data);
+
+ // Renders google.protobuf.Value in struct.proto. It picks the right oneof
+ // type based on value's type.
+ static util::Status RenderStructValue(ProtoStreamObjectWriter* ow,
+ const DataPiece& value);
+
+ // Renders google.protobuf.Timestamp value.
+ static util::Status RenderTimestamp(ProtoStreamObjectWriter* ow,
+ const DataPiece& value);
+
+ // Renders google.protobuf.FieldMask value.
+ static util::Status RenderFieldMask(ProtoStreamObjectWriter* ow,
+ const DataPiece& value);
+
+ // Renders google.protobuf.Duration value.
+ static util::Status RenderDuration(ProtoStreamObjectWriter* ow,
+ const DataPiece& value);
+
+ // Renders wrapper message types for primitive types in
+ // google/protobuf/wrappers.proto.
+ static util::Status RenderWrapperType(ProtoStreamObjectWriter* ow,
+ const DataPiece& value);
+
+ // Helper functions to create the map and find functions responsible for
+ // rendering well known types, keyed by type URL.
+ static hash_map<string, TypeRenderer>* CreateRendererMap();
+ static TypeRenderer* FindTypeRenderer(const string& type_url);
+
+ // Returns the ProtoElement::ElementType for the given Type.
+ static ProtoElement::ElementType GetElementType(
+ const google::protobuf::Type& type);
+
+ // Variables for describing the structure of the input tree:
+ // master_type_: descriptor for the whole protobuf message.
+ // typeinfo_ : the TypeInfo object to lookup types.
+ const google::protobuf::Type& master_type_;
+ TypeInfo* typeinfo_;
+ // Whether we own the typeinfo_ object.
+ bool own_typeinfo_;
+
+ // Indicates whether we finished writing root message completely.
+ bool done_;
+
+ // Variable for internal state processing:
+ // element_ : the current element.
+ // size_insert_: sizes of nested messages.
+ // pos - position to insert the size field.
+ // size - size value to be inserted.
+ google::protobuf::scoped_ptr<ProtoElement> element_;
+ std::deque<SizeInfo> size_insert_;
+
+ // Variables for output generation:
+ // output_ : pointer to an external ByteSink for final user-visible output.
+ // buffer_ : buffer holding partial message before being ready for output_.
+ // adapter_ : internal adapter between CodedOutputStream and Cord buffer_.
+ // stream_ : wrapper for writing tags and other encodings in wire format.
+ strings::ByteSink* output_;
+ string buffer_;
+ google::protobuf::io::StringOutputStream adapter_;
+ google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_;
+
+ // Variables for error tracking and reporting:
+ // listener_ : a place to report any errors found.
+ // invalid_depth_: number of enclosing invalid nested messages.
+ // tracker_ : the root location tracker interface.
+ ErrorListener* listener_;
+ int invalid_depth_;
+ google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
new file mode 100644
index 00000000..bd4f29f5
--- /dev/null
+++ b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
@@ -0,0 +1,1513 @@
+// 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 <google/protobuf/util/internal/protostream_objectwriter.h>
+
+#include <stddef.h> // For size_t
+
+#include <google/protobuf/field_mask.pb.h>
+#include <google/protobuf/timestamp.pb.h>
+#include <google/protobuf/wrappers.pb.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/util/internal/mock_error_listener.h>
+#include <google/protobuf/util/internal/testdata/books.pb.h>
+#include <google/protobuf/util/internal/testdata/field_mask.pb.h>
+#include <google/protobuf/util/internal/type_info_test_helper.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/stubs/bytestream.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/util/internal/testdata/anys.pb.h>
+#include <google/protobuf/util/internal/testdata/maps.pb.h>
+#include <google/protobuf/util/internal/testdata/struct.pb.h>
+#include <google/protobuf/util/internal/testdata/timestamp_duration.pb.h>
+#include <gtest/gtest.h>
+
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+using google::protobuf::testing::Author;
+using google::protobuf::testing::Book;
+using google::protobuf::testing::Book_Data;
+using google::protobuf::testing::Primitive;
+using google::protobuf::testing::Publisher;
+using google::protobuf::Descriptor;
+using google::protobuf::DescriptorPool;
+using google::protobuf::DynamicMessageFactory;
+using google::protobuf::FileDescriptorProto;
+using google::protobuf::Message;
+using google::protobuf::io::ArrayInputStream;
+using strings::GrowingArrayByteSink;
+using ::testing::_;
+using ::testing::Args;
+using google::protobuf::testing::anys::AnyM;
+using google::protobuf::testing::anys::AnyOut;
+using google::protobuf::testing::FieldMaskTest;
+using google::protobuf::testing::maps::MapIn;
+using google::protobuf::testing::structs::StructType;
+using google::protobuf::testing::timestampduration::TimestampDuration;
+
+
+namespace {
+string GetTypeUrl(const Descriptor* descriptor) {
+ return string(kTypeServiceBaseUrl) + "/" + descriptor->full_name();
+}
+} // namespace
+
+class BaseProtoStreamObjectWriterTest
+ : public ::testing::TestWithParam<testing::TypeInfoSource> {
+ protected:
+ BaseProtoStreamObjectWriterTest()
+ : helper_(GetParam()),
+ listener_(),
+ output_(new GrowingArrayByteSink(1000)),
+ ow_() {}
+
+ explicit BaseProtoStreamObjectWriterTest(const Descriptor* descriptor)
+ : helper_(GetParam()),
+ listener_(),
+ output_(new GrowingArrayByteSink(1000)),
+ ow_() {
+ vector<const Descriptor*> descriptors;
+ descriptors.push_back(descriptor);
+ ResetTypeInfo(descriptors);
+ }
+
+ explicit BaseProtoStreamObjectWriterTest(
+ vector<const Descriptor*> descriptors)
+ : helper_(GetParam()),
+ listener_(),
+ output_(new GrowingArrayByteSink(1000)),
+ ow_() {
+ ResetTypeInfo(descriptors);
+ }
+
+ void ResetTypeInfo(vector<const Descriptor*> descriptors) {
+ GOOGLE_CHECK(!descriptors.empty()) << "Must have at least one descriptor!";
+ helper_.ResetTypeInfo(descriptors);
+ ow_.reset(helper_.NewProtoWriter(GetTypeUrl(descriptors[0]), output_.get(),
+ &listener_));
+ }
+
+ virtual ~BaseProtoStreamObjectWriterTest() {}
+
+ void CheckOutput(const Message& expected, int expected_length) {
+ size_t nbytes;
+ google::protobuf::scoped_array<char> buffer(output_->GetBuffer(&nbytes));
+ if (expected_length >= 0) {
+ EXPECT_EQ(expected_length, nbytes);
+ }
+ string str(buffer.get(), nbytes);
+
+ std::stringbuf str_buf(str, std::ios_base::in);
+ std::istream istream(&str_buf);
+ google::protobuf::scoped_ptr<Message> message(expected.New());
+ message->ParsePartialFromIstream(&istream);
+
+ EXPECT_EQ(expected.DebugString(), message->DebugString());
+ }
+
+ void CheckOutput(const Message& expected) { CheckOutput(expected, -1); }
+
+ const google::protobuf::Type* GetType(const Descriptor* descriptor) {
+ return helper_.GetTypeInfo()->GetType(GetTypeUrl(descriptor));
+ }
+
+ testing::TypeInfoTestHelper helper_;
+ MockErrorListener listener_;
+ google::protobuf::scoped_ptr<GrowingArrayByteSink> output_;
+ google::protobuf::scoped_ptr<ProtoStreamObjectWriter> ow_;
+};
+
+MATCHER_P(HasObjectLocation, expected,
+ "Verifies the expected object location") {
+ string actual = std::tr1::get<0>(arg).ToString();
+ if (actual.compare(expected) == 0) return true;
+ *result_listener << "actual location is: " << actual;
+ return false;
+}
+
+class ProtoStreamObjectWriterTest : public BaseProtoStreamObjectWriterTest {
+ protected:
+ ProtoStreamObjectWriterTest()
+ : BaseProtoStreamObjectWriterTest(Book::descriptor()) {}
+
+ virtual ~ProtoStreamObjectWriterTest() {}
+};
+
+INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest,
+ ProtoStreamObjectWriterTest,
+ ::testing::Values(
+ testing::USE_TYPE_RESOLVER));
+
+TEST_P(ProtoStreamObjectWriterTest, EmptyObject) {
+ Book empty;
+ ow_->StartObject("")->EndObject();
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, SimpleObject) {
+ string content("My content");
+
+ Book book;
+ book.set_title("My Title");
+ book.set_length(222);
+ book.set_content(content);
+
+ ow_->StartObject("")
+ ->RenderString("title", "My Title")
+ ->RenderInt32("length", 222)
+ ->RenderBytes("content", content)
+ ->EndObject();
+ CheckOutput(book);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, SimpleMessage) {
+ Book book;
+ book.set_title("Some Book");
+ book.set_length(102);
+ Publisher* publisher = book.mutable_publisher();
+ publisher->set_name("My Publisher");
+ Author* robert = book.mutable_author();
+ robert->set_alive(true);
+ robert->set_name("robert");
+ robert->add_pseudonym("bob");
+ robert->add_pseudonym("bobby");
+ robert->add_friend_()->set_name("john");
+
+ ow_->StartObject("")
+ ->RenderString("title", "Some Book")
+ ->RenderInt32("length", 102)
+ ->StartObject("publisher")
+ ->RenderString("name", "My Publisher")
+ ->EndObject()
+ ->StartObject("author")
+ ->RenderBool("alive", true)
+ ->RenderString("name", "robert")
+ ->StartList("pseudonym")
+ ->RenderString("", "bob")
+ ->RenderString("", "bobby")
+ ->EndList()
+ ->StartList("friend")
+ ->StartObject("")
+ ->RenderString("name", "john")
+ ->EndObject()
+ ->EndList()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(book);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, PrimitiveFromStringConversion) {
+ Primitive full;
+ full.set_fix32(101);
+ full.set_u32(102);
+ full.set_i32(-103);
+ full.set_sf32(-104);
+ full.set_s32(-105);
+ full.set_fix64(40000000001L);
+ full.set_u64(40000000002L);
+ full.set_i64(-40000000003L);
+ full.set_sf64(-40000000004L);
+ full.set_s64(-40000000005L);
+ full.set_str("string1");
+ full.set_bytes("Some Bytes");
+ full.set_float_(3.14f);
+ full.set_double_(-4.05L);
+ full.set_bool_(true);
+ full.add_rep_fix32(201);
+ full.add_rep_u32(202);
+ full.add_rep_i32(-203);
+ full.add_rep_sf32(-204);
+ full.add_rep_s32(-205);
+ full.add_rep_fix64(80000000001L);
+ full.add_rep_u64(80000000002L);
+ full.add_rep_i64(-80000000003L);
+ full.add_rep_sf64(-80000000004L);
+ full.add_rep_s64(-80000000005L);
+ full.add_rep_str("string2");
+ full.add_rep_bytes("More Bytes");
+ full.add_rep_float(6.14f);
+ full.add_rep_double(-8.05L);
+ full.add_rep_bool(false);
+
+ ow_.reset(helper_.NewProtoWriter(GetTypeUrl(Primitive::descriptor()),
+ output_.get(), &listener_));
+
+ ow_->StartObject("")
+ ->RenderString("fix32", "101")
+ ->RenderString("u32", "102")
+ ->RenderString("i32", "-103")
+ ->RenderString("sf32", "-104")
+ ->RenderString("s32", "-105")
+ ->RenderString("fix64", "40000000001")
+ ->RenderString("u64", "40000000002")
+ ->RenderString("i64", "-40000000003")
+ ->RenderString("sf64", "-40000000004")
+ ->RenderString("s64", "-40000000005")
+ ->RenderString("str", "string1")
+ ->RenderString("bytes", "U29tZSBCeXRlcw==") // "Some Bytes"
+ ->RenderString("float", "3.14")
+ ->RenderString("double", "-4.05")
+ ->RenderString("bool", "true")
+ ->StartList("rep_fix32")
+ ->RenderString("", "201")
+ ->EndList()
+ ->StartList("rep_u32")
+ ->RenderString("", "202")
+ ->EndList()
+ ->StartList("rep_i32")
+ ->RenderString("", "-203")
+ ->EndList()
+ ->StartList("rep_sf32")
+ ->RenderString("", "-204")
+ ->EndList()
+ ->StartList("rep_s32")
+ ->RenderString("", "-205")
+ ->EndList()
+ ->StartList("rep_fix64")
+ ->RenderString("", "80000000001")
+ ->EndList()
+ ->StartList("rep_u64")
+ ->RenderString("", "80000000002")
+ ->EndList()
+ ->StartList("rep_i64")
+ ->RenderString("", "-80000000003")
+ ->EndList()
+ ->StartList("rep_sf64")
+ ->RenderString("", "-80000000004")
+ ->EndList()
+ ->StartList("rep_s64")
+ ->RenderString("", "-80000000005")
+ ->EndList()
+ ->StartList("rep_str")
+ ->RenderString("", "string2")
+ ->EndList()
+ ->StartList("rep_bytes")
+ ->RenderString("", "TW9yZSBCeXRlcw==") // "More Bytes"
+ ->EndList()
+ ->StartList("rep_float")
+ ->RenderString("", "6.14")
+ ->EndList()
+ ->StartList("rep_double")
+ ->RenderString("", "-8.05")
+ ->EndList()
+ ->StartList("rep_bool")
+ ->RenderString("", "false")
+ ->EndList()
+ ->EndObject();
+ CheckOutput(full);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, InfinityInputTest) {
+ Primitive full;
+ full.set_double_(std::numeric_limits<double>::infinity());
+ full.set_float_(std::numeric_limits<float>::infinity());
+ full.set_str("-Infinity");
+
+ ow_.reset(helper_.NewProtoWriter(GetTypeUrl(Primitive::descriptor()),
+ output_.get(), &listener_));
+
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT32"),
+ StringPiece("\"Infinity\"")))
+ .With(Args<0>(HasObjectLocation("i32")));
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"),
+ StringPiece("\"Infinity\"")))
+ .With(Args<0>(HasObjectLocation("u32")));
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_SFIXED64"),
+ StringPiece("\"-Infinity\"")))
+ .With(Args<0>(HasObjectLocation("sf64")));
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_BOOL"),
+ StringPiece("\"Infinity\"")))
+ .With(Args<0>(HasObjectLocation("bool")));
+
+ ow_->StartObject("")
+ ->RenderString("double", "Infinity")
+ ->RenderString("float", "Infinity")
+ ->RenderString("i32", "Infinity")
+ ->RenderString("u32", "Infinity")
+ ->RenderString("sf64", "-Infinity")
+ ->RenderString("str", "-Infinity")
+ ->RenderString("bool", "Infinity")
+ ->EndObject();
+ CheckOutput(full);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, NaNInputTest) {
+ Primitive full;
+ full.set_double_(std::numeric_limits<double>::quiet_NaN());
+ full.set_float_(std::numeric_limits<float>::quiet_NaN());
+ full.set_str("NaN");
+
+ ow_.reset(helper_.NewProtoWriter(GetTypeUrl(Primitive::descriptor()),
+ output_.get(), &listener_));
+
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT32"),
+ StringPiece("\"NaN\"")))
+ .With(Args<0>(HasObjectLocation("i32")));
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"),
+ StringPiece("\"NaN\"")))
+ .With(Args<0>(HasObjectLocation("u32")));
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_SFIXED64"),
+ StringPiece("\"NaN\"")))
+ .With(Args<0>(HasObjectLocation("sf64")));
+ EXPECT_CALL(listener_,
+ InvalidValue(_, StringPiece("TYPE_BOOL"), StringPiece("\"NaN\"")))
+ .With(Args<0>(HasObjectLocation("bool")));
+
+ ow_->StartObject("")
+ ->RenderString("double", "NaN")
+ ->RenderString("float", "NaN")
+ ->RenderString("i32", "NaN")
+ ->RenderString("u32", "NaN")
+ ->RenderString("sf64", "NaN")
+ ->RenderString("str", "NaN")
+ ->RenderString("bool", "NaN")
+ ->EndObject();
+
+ CheckOutput(full);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, ImplicitPrimitiveList) {
+ Book expected;
+ Author* author = expected.mutable_author();
+ author->set_name("The Author");
+ author->add_pseudonym("first");
+ author->add_pseudonym("second");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "The Author")
+ ->RenderString("pseudonym", "first")
+ ->RenderString("pseudonym", "second")
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest,
+ LastWriteWinsOnNonRepeatedPrimitiveFieldWithDuplicates) {
+ Book expected;
+ Author* author = expected.mutable_author();
+ author->set_name("second");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "first")
+ ->RenderString("name", "second")
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, ExplicitPrimitiveList) {
+ Book expected;
+ Author* author = expected.mutable_author();
+ author->set_name("The Author");
+ author->add_pseudonym("first");
+ author->add_pseudonym("second");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "The Author")
+ ->StartList("pseudonym")
+ ->RenderString("", "first")
+ ->RenderString("", "second")
+ ->EndList()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, NonRepeatedExplicitPrimitiveList) {
+ Book expected;
+ expected.set_allocated_author(new Author());
+
+ EXPECT_CALL(
+ listener_,
+ InvalidName(
+ _, StringPiece("name"),
+ StringPiece("Proto field is not repeating, cannot start list.")))
+ .With(Args<0>(HasObjectLocation("author")));
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->StartList("name")
+ ->RenderString("", "first")
+ ->RenderString("", "second")
+ ->EndList()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, ImplicitMessageList) {
+ Book expected;
+ Author* outer = expected.mutable_author();
+ outer->set_name("outer");
+ outer->set_alive(true);
+ Author* first = outer->add_friend_();
+ first->set_name("first");
+ Author* second = outer->add_friend_();
+ second->set_name("second");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "outer")
+ ->RenderBool("alive", true)
+ ->StartObject("friend")
+ ->RenderString("name", "first")
+ ->EndObject()
+ ->StartObject("friend")
+ ->RenderString("name", "second")
+ ->EndObject()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest,
+ LastWriteWinsOnNonRepeatedMessageFieldWithDuplicates) {
+ Book expected;
+ Author* author = expected.mutable_author();
+ author->set_name("The Author");
+ Publisher* publisher = expected.mutable_publisher();
+ publisher->set_name("second");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "The Author")
+ ->EndObject()
+ ->StartObject("publisher")
+ ->RenderString("name", "first")
+ ->EndObject()
+ ->StartObject("publisher")
+ ->RenderString("name", "second")
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, ExplicitMessageList) {
+ Book expected;
+ Author* outer = expected.mutable_author();
+ outer->set_name("outer");
+ outer->set_alive(true);
+ Author* first = outer->add_friend_();
+ first->set_name("first");
+ Author* second = outer->add_friend_();
+ second->set_name("second");
+
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "outer")
+ ->RenderBool("alive", true)
+ ->StartList("friend")
+ ->StartObject("")
+ ->RenderString("name", "first")
+ ->EndObject()
+ ->StartObject("")
+ ->RenderString("name", "second")
+ ->EndObject()
+ ->EndList()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, NonRepeatedExplicitMessageList) {
+ Book expected;
+ Author* author = expected.mutable_author();
+ author->set_name("The Author");
+
+ EXPECT_CALL(
+ listener_,
+ InvalidName(
+ _, StringPiece("publisher"),
+ StringPiece("Proto field is not repeating, cannot start list.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "The Author")
+ ->EndObject()
+ ->StartList("publisher")
+ ->StartObject("")
+ ->RenderString("name", "first")
+ ->EndObject()
+ ->StartObject("")
+ ->RenderString("name", "second")
+ ->EndObject()
+ ->EndList()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnknownFieldAtRoot) {
+ Book empty;
+
+ EXPECT_CALL(listener_, InvalidName(_, StringPiece("unknown"),
+ StringPiece("Cannot find field.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("")->RenderString("unknown", "Nope!")->EndObject();
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnknownFieldAtAuthorFriend) {
+ Book expected;
+ Author* paul = expected.mutable_author();
+ paul->set_name("Paul");
+ Author* mark = paul->add_friend_();
+ mark->set_name("Mark");
+ Author* john = paul->add_friend_();
+ john->set_name("John");
+ Author* luke = paul->add_friend_();
+ luke->set_name("Luke");
+
+ EXPECT_CALL(listener_, InvalidName(_, StringPiece("address"),
+ StringPiece("Cannot find field.")))
+ .With(Args<0>(HasObjectLocation("author.friend[1]")));
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "Paul")
+ ->StartList("friend")
+ ->StartObject("")
+ ->RenderString("name", "Mark")
+ ->EndObject()
+ ->StartObject("")
+ ->RenderString("name", "John")
+ ->RenderString("address", "Patmos")
+ ->EndObject()
+ ->StartObject("")
+ ->RenderString("name", "Luke")
+ ->EndObject()
+ ->EndList()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnknownObjectAtRoot) {
+ Book empty;
+
+ EXPECT_CALL(listener_, InvalidName(_, StringPiece("unknown"),
+ StringPiece("Cannot find field.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("")->StartObject("unknown")->EndObject()->EndObject();
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnknownObjectAtAuthor) {
+ Book expected;
+ Author* author = expected.mutable_author();
+ author->set_name("William");
+ author->add_pseudonym("Bill");
+
+ EXPECT_CALL(listener_, InvalidName(_, StringPiece("wife"),
+ StringPiece("Cannot find field.")))
+ .With(Args<0>(HasObjectLocation("author")));
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderString("name", "William")
+ ->StartObject("wife")
+ ->RenderString("name", "Hilary")
+ ->EndObject()
+ ->RenderString("pseudonym", "Bill")
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnknownListAtRoot) {
+ Book empty;
+
+ EXPECT_CALL(listener_, InvalidName(_, StringPiece("unknown"),
+ StringPiece("Cannot find field.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("")->StartList("unknown")->EndList()->EndObject();
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnknownListAtPublisher) {
+ Book expected;
+ expected.set_title("Brainwashing");
+ Publisher* publisher = expected.mutable_publisher();
+ publisher->set_name("propaganda");
+
+ EXPECT_CALL(listener_, InvalidName(_, StringPiece("alliance"),
+ StringPiece("Cannot find field.")))
+ .With(Args<0>(HasObjectLocation("publisher")));
+ ow_->StartObject("")
+ ->StartObject("publisher")
+ ->RenderString("name", "propaganda")
+ ->StartList("alliance")
+ ->EndList()
+ ->EndObject()
+ ->RenderString("title", "Brainwashing")
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, MissingRequiredField) {
+ Book expected;
+ expected.set_title("My Title");
+ expected.set_allocated_publisher(new Publisher());
+
+ EXPECT_CALL(listener_, MissingField(_, StringPiece("name")))
+ .With(Args<0>(HasObjectLocation("publisher")));
+ ow_->StartObject("")
+ ->StartObject("publisher")
+ ->EndObject()
+ ->RenderString("title", "My Title")
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, InvalidFieldValueAtRoot) {
+ Book empty;
+
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"),
+ StringPiece("\"garbage\"")))
+ .With(Args<0>(HasObjectLocation("length")));
+ ow_->StartObject("")->RenderString("length", "garbage")->EndObject();
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, MultipleInvalidFieldValues) {
+ Book expected;
+ expected.set_title("My Title");
+
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_UINT32"),
+ StringPiece("\"-400\"")))
+ .With(Args<0>(HasObjectLocation("length")));
+ EXPECT_CALL(listener_, InvalidValue(_, StringPiece("TYPE_INT64"),
+ StringPiece("\"3.14\"")))
+ .With(Args<0>(HasObjectLocation("published")));
+ ow_->StartObject("")
+ ->RenderString("length", "-400")
+ ->RenderString("published", "3.14")
+ ->RenderString("title", "My Title")
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnnamedFieldAtRoot) {
+ Book empty;
+
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece(""),
+ StringPiece("Proto fields must have a name.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("")->RenderFloat("", 3.14)->EndObject();
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnnamedFieldAtAuthor) {
+ Book expected;
+ expected.set_title("noname");
+ expected.set_allocated_author(new Author());
+
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece(""),
+ StringPiece("Proto fields must have a name.")))
+ .With(Args<0>(HasObjectLocation("author")));
+ ow_->StartObject("")
+ ->StartObject("author")
+ ->RenderInt32("", 123)
+ ->EndObject()
+ ->RenderString("title", "noname")
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, UnnamedListAtRoot) {
+ Book expected;
+ expected.set_title("noname");
+
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece(""),
+ StringPiece("Proto fields must have a name.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("")
+ ->StartList("")
+ ->EndList()
+ ->RenderString("title", "noname")
+ ->EndObject();
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, RootNamedObject) {
+ Book expected;
+ expected.set_title("Annie");
+
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece("oops"),
+ StringPiece("Root element should not be named.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("oops")->RenderString("title", "Annie")->EndObject();
+ CheckOutput(expected, 7);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, RootNamedList) {
+ Book empty;
+
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece("oops"),
+ StringPiece("Root element should not be named.")))
+ .With(Args<0>(HasObjectLocation("")));
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece(""),
+ StringPiece("Proto fields must have a name.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartList("oops")->RenderString("", "item")->EndList();
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, RootUnnamedField) {
+ Book empty;
+
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece(""),
+ StringPiece("Root element must be a message.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->RenderBool("", true);
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, RootNamedField) {
+ Book empty;
+
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece("oops"),
+ StringPiece("Root element must be a message.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->RenderBool("oops", true);
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, NullValue) {
+ Book empty;
+
+ ow_->RenderNull("");
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, NullValueForMessageField) {
+ Book empty;
+
+ ow_->RenderNull("author");
+ CheckOutput(empty, 0);
+}
+
+TEST_P(ProtoStreamObjectWriterTest, NullValueForPrimitiveField) {
+ Book empty;
+
+ ow_->RenderNull("length");
+ CheckOutput(empty, 0);
+}
+
+class ProtoStreamObjectWriterTimestampDurationTest
+ : public BaseProtoStreamObjectWriterTest {
+ protected:
+ ProtoStreamObjectWriterTimestampDurationTest() {
+ vector<const Descriptor*> descriptors;
+ descriptors.push_back(TimestampDuration::descriptor());
+ descriptors.push_back(google::protobuf::Timestamp::descriptor());
+ descriptors.push_back(google::protobuf::Duration::descriptor());
+ ResetTypeInfo(descriptors);
+ }
+};
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece(
+ "Field 'ts', Illegal timestamp format; timestamps "
+ "must end with 'Z'")));
+
+ ow_->StartObject("")->RenderString("ts", "")->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError2) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(
+ _, StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece(
+ "Field 'ts', Invalid time format: Failed to parse input")));
+
+ ow_->StartObject("")->RenderString("ts", "Z")->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError3) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece(
+ "Field 'ts', Invalid time format, failed to parse nano "
+ "seconds")));
+
+ ow_->StartObject("")
+ ->RenderString("ts", "1970-01-01T00:00:00.ABZ")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError4) {
+ TimestampDuration timestamp;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece("Field 'ts', Timestamp value exceeds limits")));
+
+ ow_->StartObject("")
+ ->RenderString("ts", "-8032-10-18T00:00:00.000Z")
+ ->EndObject();
+ CheckOutput(timestamp);
+}
+
+// TODO(skarvaje): Write a test for nanos that exceed limit. Currently, it is
+// not possible to construct a test case where nanos exceed limit because of
+// floating point arithmetic.
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError1) {
+ TimestampDuration duration;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Duration"),
+ StringPiece(
+ "Field 'dur', Illegal duration format; duration must "
+ "end with 's'")));
+
+ ow_->StartObject("")->RenderString("dur", "")->EndObject();
+ CheckOutput(duration);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError2) {
+ TimestampDuration duration;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Duration"),
+ StringPiece(
+ "Field 'dur', Invalid duration format, failed to parse "
+ "seconds")));
+
+ ow_->StartObject("")->RenderString("dur", "s")->EndObject();
+ CheckOutput(duration);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError3) {
+ TimestampDuration duration;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Duration"),
+ StringPiece(
+ "Field 'dur', Invalid duration format, failed to "
+ "parse nanos seconds")));
+
+ ow_->StartObject("")->RenderString("dur", "123.DEFs")->EndObject();
+ CheckOutput(duration);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError4) {
+ TimestampDuration duration;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.Duration"),
+ StringPiece("Field 'dur', Duration value exceeds limits")));
+
+ ow_->StartObject("")->RenderString("dur", "315576000002s")->EndObject();
+ CheckOutput(duration);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest,
+ MismatchedTimestampTypeInput) {
+ TimestampDuration timestamp;
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(
+ _, StringPiece("type.googleapis.com/google.protobuf.Timestamp"),
+ StringPiece(
+ "Field 'ts', Invalid data type for timestamp, value is null")))
+ .With(Args<0>(HasObjectLocation("ts")));
+ ow_->StartObject("")->RenderNull("ts")->EndObject();
+ CheckOutput(timestamp);
+}
+
+TEST_P(ProtoStreamObjectWriterTimestampDurationTest,
+ MismatchedDurationTypeInput) {
+ TimestampDuration duration;
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(
+ _, StringPiece("type.googleapis.com/google.protobuf.Duration"),
+ StringPiece(
+ "Field 'dur', Invalid data type for duration, value is null")))
+ .With(Args<0>(HasObjectLocation("dur")));
+ ow_->StartObject("")->RenderNull("dur")->EndObject();
+ CheckOutput(duration);
+}
+
+class ProtoStreamObjectWriterStructTest
+ : public BaseProtoStreamObjectWriterTest {
+ protected:
+ ProtoStreamObjectWriterStructTest() {
+ vector<const Descriptor*> descriptors;
+ descriptors.push_back(StructType::descriptor());
+ descriptors.push_back(google::protobuf::Struct::descriptor());
+ ResetTypeInfo(descriptors);
+ }
+};
+
+// TODO(skarvaje): Write tests for failure cases.
+TEST_P(ProtoStreamObjectWriterStructTest, StructRenderSuccess) {
+ StructType struct_type;
+ google::protobuf::Struct* s = struct_type.mutable_object();
+ s->mutable_fields()->operator[]("k1").set_number_value(123);
+ s->mutable_fields()->operator[]("k2").set_bool_value(true);
+
+ ow_->StartObject("")
+ ->StartObject("object")
+ ->RenderDouble("k1", 123)
+ ->RenderBool("k2", true)
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(struct_type);
+}
+
+TEST_P(ProtoStreamObjectWriterStructTest, StructNullInputSuccess) {
+ StructType struct_type;
+ EXPECT_CALL(listener_,
+ InvalidName(_, StringPiece(""),
+ StringPiece("Proto fields must have a name.")))
+ .With(Args<0>(HasObjectLocation("")));
+ ow_->StartObject("")->RenderNull("")->EndObject();
+ CheckOutput(struct_type);
+}
+
+TEST_P(ProtoStreamObjectWriterStructTest, StructInvalidInputFailure) {
+ StructType struct_type;
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.Struct"),
+ StringPiece("true")))
+ .With(Args<0>(HasObjectLocation("object")));
+
+ ow_->StartObject("")->RenderBool("object", true)->EndObject();
+ CheckOutput(struct_type);
+}
+
+class ProtoStreamObjectWriterMapTest : public BaseProtoStreamObjectWriterTest {
+ protected:
+ ProtoStreamObjectWriterMapTest()
+ : BaseProtoStreamObjectWriterTest(MapIn::descriptor()) {}
+};
+
+TEST_P(ProtoStreamObjectWriterMapTest, MapShouldNotAcceptList) {
+ MapIn mm;
+ EXPECT_CALL(listener_,
+ InvalidValue(_, StringPiece("Map"),
+ StringPiece("Cannot bind a list to map.")))
+ .With(Args<0>(HasObjectLocation("map_input")));
+ ow_->StartObject("")
+ ->StartList("map_input")
+ ->RenderString("a", "b")
+ ->EndList()
+ ->EndObject();
+ CheckOutput(mm);
+}
+
+class ProtoStreamObjectWriterAnyTest : public BaseProtoStreamObjectWriterTest {
+ protected:
+ ProtoStreamObjectWriterAnyTest() {
+ vector<const Descriptor*> descriptors;
+ descriptors.push_back(AnyOut::descriptor());
+ descriptors.push_back(google::protobuf::DoubleValue::descriptor());
+ descriptors.push_back(google::protobuf::Any::descriptor());
+ ResetTypeInfo(descriptors);
+ }
+};
+
+TEST_P(ProtoStreamObjectWriterAnyTest, AnyRenderSuccess) {
+ AnyOut any;
+ google::protobuf::Any* any_type = any.mutable_any();
+ any_type->set_type_url("type.googleapis.com/google.protobuf.DoubleValue");
+ google::protobuf::DoubleValue d;
+ d.set_value(40.2);
+ any_type->set_value(d.SerializeAsString());
+
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "type.googleapis.com/google.protobuf.DoubleValue")
+ ->RenderDouble("value", 40.2)
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(any);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, RecursiveAny) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+ any->set_type_url("type.googleapis.com/google.protobuf.Any");
+
+ ::google::protobuf::Any nested_any;
+ nested_any.set_type_url(
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM");
+
+ AnyM m;
+ m.set_foo("foovalue");
+ nested_any.set_value(m.SerializeAsString());
+
+ any->set_value(nested_any.SerializeAsString());
+
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
+ ->StartObject("value")
+ ->RenderString("@type",
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM")
+ ->RenderString("foo", "foovalue")
+ ->EndObject()
+ ->EndObject()
+ ->EndObject();
+
+ CheckOutput(out, 115);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, DoubleRecursiveAny) {
+ AnyOut out;
+ ::google::protobuf::Any* any = out.mutable_any();
+ any->set_type_url("type.googleapis.com/google.protobuf.Any");
+
+ ::google::protobuf::Any nested_any;
+ nested_any.set_type_url("type.googleapis.com/google.protobuf.Any");
+
+ ::google::protobuf::Any second_nested_any;
+ second_nested_any.set_type_url(
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM");
+
+ AnyM m;
+ m.set_foo("foovalue");
+ second_nested_any.set_value(m.SerializeAsString());
+
+ nested_any.set_value(second_nested_any.SerializeAsString());
+ any->set_value(nested_any.SerializeAsString());
+
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
+ ->StartObject("value")
+ ->RenderString("@type", "type.googleapis.com/google.protobuf.Any")
+ ->StartObject("value")
+ ->RenderString("@type",
+ "type.googleapis.com/google.protobuf.testing.anys.AnyM")
+ ->RenderString("foo", "foovalue")
+ ->EndObject()
+ ->EndObject()
+ ->EndObject()
+ ->EndObject();
+
+ CheckOutput(out, 159);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, EmptyAnyFromEmptyObject) {
+ AnyOut out;
+ out.mutable_any();
+
+ ow_->StartObject("")->StartObject("any")->EndObject()->EndObject();
+
+ CheckOutput(out, 2);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails1) {
+ AnyOut any;
+
+ EXPECT_CALL(listener_,
+ InvalidValue(_, StringPiece("Any"),
+ StringPiece(
+ "Missing or invalid @type for any field in "
+ "google.protobuf.testing.anys.AnyOut")));
+
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->StartObject("another")
+ ->EndObject()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(any);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails2) {
+ AnyOut any;
+
+ EXPECT_CALL(listener_,
+ InvalidValue(_, StringPiece("Any"),
+ StringPiece(
+ "Missing or invalid @type for any field in "
+ "google.protobuf.testing.anys.AnyOut")));
+
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->StartList("another")
+ ->EndObject()
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(any);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails3) {
+ AnyOut any;
+
+ EXPECT_CALL(listener_,
+ InvalidValue(_, StringPiece("Any"),
+ StringPiece(
+ "Missing or invalid @type for any field in "
+ "google.protobuf.testing.anys.AnyOut")));
+
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->RenderString("value", "somevalue")
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(any);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithInvalidTypeUrlFails) {
+ AnyOut any;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_, StringPiece("Any"),
+ StringPiece(
+ "Invalid type URL, type URLs must be of the form "
+ "'type.googleapis.com/<typename>', got: "
+ "type.other.com/some.Type")));
+
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "type.other.com/some.Type")
+ ->RenderDouble("value", 40.2)
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(any);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithUnknownTypeFails) {
+ AnyOut any;
+
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_, StringPiece("Any"),
+ StringPiece("Invalid type URL, unknown type: some.Type")));
+ ow_->StartObject("")
+ ->StartObject("any")
+ ->RenderString("@type", "type.googleapis.com/some.Type")
+ ->RenderDouble("value", 40.2)
+ ->EndObject()
+ ->EndObject();
+ CheckOutput(any);
+}
+
+TEST_P(ProtoStreamObjectWriterAnyTest, AnyNullInputFails) {
+ AnyOut any;
+
+ ow_->StartObject("")->RenderNull("any")->EndObject();
+ CheckOutput(any);
+}
+
+class ProtoStreamObjectWriterFieldMaskTest
+ : public BaseProtoStreamObjectWriterTest {
+ protected:
+ ProtoStreamObjectWriterFieldMaskTest() {
+ vector<const Descriptor*> descriptors;
+ descriptors.push_back(FieldMaskTest::descriptor());
+ descriptors.push_back(google::protobuf::FieldMask::descriptor());
+ ResetTypeInfo(descriptors);
+ }
+};
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, SimpleFieldMaskTest) {
+ FieldMaskTest expected;
+ expected.set_id("1");
+ expected.mutable_single_mask()->add_paths("path1");
+
+ ow_->StartObject("");
+ ow_->RenderString("id", "1");
+ ow_->RenderString("single_mask", "path1");
+ ow_->EndObject();
+
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, MutipleMasksInCompactForm) {
+ FieldMaskTest expected;
+ expected.set_id("1");
+ expected.mutable_single_mask()->add_paths("camel_case1");
+ expected.mutable_single_mask()->add_paths("camel_case2");
+ expected.mutable_single_mask()->add_paths("camel_case3");
+
+ ow_->StartObject("");
+ ow_->RenderString("id", "1");
+ ow_->RenderString("single_mask", "camelCase1,camelCase2,camelCase3");
+ ow_->EndObject();
+
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, RepeatedFieldMaskTest) {
+ FieldMaskTest expected;
+ expected.set_id("1");
+ google::protobuf::FieldMask* mask = expected.add_repeated_mask();
+ mask->add_paths("field1");
+ mask->add_paths("field2");
+ expected.add_repeated_mask()->add_paths("field3");
+
+ ow_->StartObject("");
+ ow_->RenderString("id", "1");
+ ow_->StartList("repeated_mask");
+ ow_->RenderString("", "field1,field2");
+ ow_->RenderString("", "field3");
+ ow_->EndList();
+ ow_->EndObject();
+
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, EmptyFieldMaskTest) {
+ FieldMaskTest expected;
+ expected.set_id("1");
+
+ ow_->StartObject("");
+ ow_->RenderString("id", "1");
+ ow_->RenderString("single_mask", "");
+ ow_->EndObject();
+
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, MaskUsingApiaryStyleShouldWork) {
+ FieldMaskTest expected;
+ expected.set_id("1");
+
+ ow_->StartObject("");
+ ow_->RenderString("id", "1");
+ // Case1
+ ow_->RenderString("single_mask",
+ "outerField(camelCase1,camelCase2,camelCase3)");
+ expected.mutable_single_mask()->add_paths("outer_field.camel_case1");
+ expected.mutable_single_mask()->add_paths("outer_field.camel_case2");
+ expected.mutable_single_mask()->add_paths("outer_field.camel_case3");
+
+ ow_->StartList("repeated_mask");
+
+ ow_->RenderString("", "a(field1,field2)");
+ google::protobuf::FieldMask* mask = expected.add_repeated_mask();
+ mask->add_paths("a.field1");
+ mask->add_paths("a.field2");
+
+ ow_->RenderString("", "a(field3)");
+ mask = expected.add_repeated_mask();
+ mask->add_paths("a.field3");
+
+ ow_->RenderString("", "a()");
+ expected.add_repeated_mask();
+
+ ow_->RenderString("", "a(,)");
+ expected.add_repeated_mask();
+
+ ow_->RenderString("", "a(field1(field2(field3)))");
+ mask = expected.add_repeated_mask();
+ mask->add_paths("a.field1.field2.field3");
+
+ ow_->RenderString("", "a(field1(field2(field3,field4),field5),field6)");
+ mask = expected.add_repeated_mask();
+ mask->add_paths("a.field1.field2.field3");
+ mask->add_paths("a.field1.field2.field4");
+ mask->add_paths("a.field1.field5");
+ mask->add_paths("a.field6");
+
+ ow_->RenderString("", "a(id,field1(id,field2(field3,field4),field5),field6)");
+ mask = expected.add_repeated_mask();
+ mask->add_paths("a.id");
+ mask->add_paths("a.field1.id");
+ mask->add_paths("a.field1.field2.field3");
+ mask->add_paths("a.field1.field2.field4");
+ mask->add_paths("a.field1.field5");
+ mask->add_paths("a.field6");
+
+ ow_->RenderString("", "a(((field3,field4)))");
+ mask = expected.add_repeated_mask();
+ mask->add_paths("a.field3");
+ mask->add_paths("a.field4");
+
+ ow_->EndList();
+ ow_->EndObject();
+
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, MoreCloseThanOpenParentheses) {
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.FieldMask"),
+ StringPiece(
+ "Field 'single_mask', Invalid FieldMask 'a(b,c))'. "
+ "Cannot find matching '(' for all ')'.")));
+
+ ow_->StartObject("");
+ ow_->RenderString("id", "1");
+ ow_->RenderString("single_mask", "a(b,c))");
+ ow_->EndObject();
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, MoreOpenThanCloseParentheses) {
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(
+ _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"),
+ StringPiece(
+ "Field 'single_mask', Invalid FieldMask 'a(((b,c)'. Cannot "
+ "find matching ')' for all '('.")));
+
+ ow_->StartObject("");
+ ow_->RenderString("id", "1");
+ ow_->RenderString("single_mask", "a(((b,c)");
+ ow_->EndObject();
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, PathWithMapKeyShouldWork) {
+ FieldMaskTest expected;
+ expected.mutable_single_mask()->add_paths("path.to.map[\"key1\"]");
+ expected.mutable_single_mask()->add_paths(
+ "path.to.map[\"e\\\"[]][scape\\\"\"]");
+ expected.mutable_single_mask()->add_paths("path.to.map[\"key2\"]");
+
+ ow_->StartObject("");
+ ow_->RenderString("single_mask",
+ "path.to.map[\"key1\"],path.to.map[\"e\\\"[]][scape\\\"\"],"
+ "path.to.map[\"key2\"]");
+ ow_->EndObject();
+
+ CheckOutput(expected);
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest,
+ MapKeyMustBeAtTheEndOfAPathSegment) {
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.FieldMask"),
+ StringPiece(
+ "Field 'single_mask', Invalid FieldMask "
+ "'path.to.map[\"key1\"]a,path.to.map[\"key2\"]'. "
+ "Map keys should be at the end of a path segment.")));
+
+ ow_->StartObject("");
+ ow_->RenderString("single_mask",
+ "path.to.map[\"key1\"]a,path.to.map[\"key2\"]");
+ ow_->EndObject();
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustEnd) {
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.FieldMask"),
+ StringPiece(
+ "Field 'single_mask', Invalid FieldMask "
+ "'path.to.map[\"key1\"'. Map keys should be "
+ "represented as [\"some_key\"].")));
+
+ ow_->StartObject("");
+ ow_->RenderString("single_mask", "path.to.map[\"key1\"");
+ ow_->EndObject();
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustBeEscapedCorrectly) {
+ EXPECT_CALL(
+ listener_,
+ InvalidValue(_,
+ StringPiece("type.googleapis.com/google.protobuf.FieldMask"),
+ StringPiece(
+ "Field 'single_mask', Invalid FieldMask "
+ "'path.to.map[\"ke\"y1\"]'. Map keys should be "
+ "represented as [\"some_key\"].")));
+
+ ow_->StartObject("");
+ ow_->RenderString("single_mask", "path.to.map[\"ke\"y1\"]");
+ ow_->EndObject();
+}
+
+TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyCanContainAnyChars) {
+ FieldMaskTest expected;
+ expected.mutable_single_mask()->add_paths(
+ "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War孙天涌,./?><\\\\\"]");
+ expected.mutable_single_mask()->add_paths("path.to.map[\"key2\"]");
+
+ ow_->StartObject("");
+ ow_->RenderString(
+ "single_mask",
+ "path.to.map[\"(),[],\\\"'!@#$%^&*123_|War孙天涌,./?><\\\\\"],"
+ "path.to.map[\"key2\"]");
+ ow_->EndObject();
+
+ CheckOutput(expected);
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter.h b/src/google/protobuf/util/internal/snake2camel_objectwriter.h
new file mode 100644
index 00000000..1a32bc56
--- /dev/null
+++ b/src/google/protobuf/util/internal/snake2camel_objectwriter.h
@@ -0,0 +1,187 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/util/internal/object_writer.h>
+#include <google/protobuf/util/internal/utility.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// Snake2CamelObjectWriter is an ObjectWriter than translates each field name
+// from snake_case to camelCase. Typical usage is:
+// ProtoStreamObjectSource psos(...);
+// JsonObjectWriter jow(...);
+// Snake2CamelObjectWriter snake_to_camel(&jow);
+// psos.writeTo(&snake_to_camel);
+class Snake2CamelObjectWriter : public ObjectWriter {
+ public:
+ explicit Snake2CamelObjectWriter(ObjectWriter* ow)
+ : ow_(ow), normalize_case_(true) {}
+ virtual ~Snake2CamelObjectWriter() {}
+
+ // ObjectWriter methods.
+ virtual Snake2CamelObjectWriter* StartObject(StringPiece name) {
+ ow_->StartObject(ShouldNormalizeCase(name)
+ ? StringPiece(StringPiece(ToCamelCase(name)))
+ : name);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* EndObject() {
+ ow_->EndObject();
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* StartList(StringPiece name) {
+ ow_->StartList(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name))
+ : name);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* EndList() {
+ ow_->EndList();
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderBool(StringPiece name, bool value) {
+ ow_->RenderBool(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderInt32(StringPiece name, int32 value) {
+ ow_->RenderInt32(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderUint32(StringPiece name,
+ uint32 value) {
+ ow_->RenderUint32(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderInt64(StringPiece name, int64 value) {
+ ow_->RenderInt64(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderUint64(StringPiece name,
+ uint64 value) {
+ ow_->RenderUint64(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderDouble(StringPiece name,
+ double value) {
+ ow_->RenderDouble(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderFloat(StringPiece name, float value) {
+ ow_->RenderFloat(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderString(StringPiece name,
+ StringPiece value) {
+ ow_->RenderString(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderBytes(StringPiece name,
+ StringPiece value) {
+ ow_->RenderBytes(
+ ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name,
+ value);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* RenderNull(StringPiece name) {
+ ow_->RenderNull(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name))
+ : name);
+ return this;
+ }
+
+ virtual Snake2CamelObjectWriter* DisableCaseNormalizationForNextKey() {
+ normalize_case_ = false;
+ return this;
+ }
+
+ private:
+ ObjectWriter* ow_;
+ bool normalize_case_;
+
+ bool ShouldNormalizeCase(StringPiece name) {
+ if (normalize_case_) {
+ return !IsCamel(name);
+ } else {
+ normalize_case_ = true;
+ return false;
+ }
+ }
+
+ bool IsCamel(StringPiece name) {
+ return name.empty() || (ascii_islower(name[0]) && !name.contains("_"));
+ }
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Snake2CamelObjectWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc b/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc
new file mode 100644
index 00000000..67388c3b
--- /dev/null
+++ b/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc
@@ -0,0 +1,311 @@
+// 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 <google/protobuf/util/internal/snake2camel_objectwriter.h>
+#include <google/protobuf/util/internal/expecting_objectwriter.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+class Snake2CamelObjectWriterTest : public ::testing::Test {
+ protected:
+ Snake2CamelObjectWriterTest() : mock_(), expects_(&mock_), testing_(&mock_) {}
+ virtual ~Snake2CamelObjectWriterTest() {}
+
+ MockObjectWriter mock_;
+ ExpectingObjectWriter expects_;
+ Snake2CamelObjectWriter testing_;
+};
+
+TEST_F(Snake2CamelObjectWriterTest, Empty) {
+ // Set expectation
+ expects_.StartObject("")->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, UnderscoresOnly) {
+ // Set expectation
+ expects_.StartObject("")
+ ->RenderInt32("", 1)
+ ->RenderInt32("", 2)
+ ->RenderInt32("", 3)
+ ->RenderInt32("", 4)
+ ->RenderInt32("", 5)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")
+ ->RenderInt32("_", 1)
+ ->RenderInt32("__", 2)
+ ->RenderInt32("___", 3)
+ ->RenderInt32("____", 4)
+ ->RenderInt32("_____", 5)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, LowercaseOnly) {
+ // Set expectation
+ expects_.StartObject("")
+ ->RenderString("key", "value")
+ ->RenderString("abracadabra", "magic")
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")
+ ->RenderString("key", "value")
+ ->RenderString("abracadabra", "magic")
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, UppercaseOnly) {
+ // Set expectation
+ expects_.StartObject("")
+ ->RenderString("key", "VALUE")
+ ->RenderString("abracadabra", "MAGIC")
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")
+ ->RenderString("KEY", "VALUE")
+ ->RenderString("ABRACADABRA", "MAGIC")
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, CamelCase) {
+ // Set expectation
+ expects_.StartObject("")
+ ->RenderString("camelCase", "camelCase")
+ ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog",
+ "theQuickBrownFoxJumpsOverTheLazyDog")
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")
+ ->RenderString("camelCase", "camelCase")
+ ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog",
+ "theQuickBrownFoxJumpsOverTheLazyDog")
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, FirstCapCamelCase) {
+ // Sets expectation
+ expects_.StartObject("camel")
+ ->RenderString("camelCase", "CamelCase")
+ ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog",
+ "TheQuickBrownFoxJumpsOverTheLazyDog")
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("Camel")
+ ->RenderString("CamelCase", "CamelCase")
+ ->RenderString("TheQuickBrownFoxJumpsOverTheLazyDog",
+ "TheQuickBrownFoxJumpsOverTheLazyDog")
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, LastCapCamelCase) {
+ // Sets expectation
+ expects_.StartObject("lastCapCamelCasE")->EndObject();
+
+ // Actual testing
+ testing_.StartObject("lastCapCamelCasE")->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, MixedCapCamelCase) {
+ // Sets expectation
+ expects_.StartObject("googleIsTheBest")
+ ->RenderFloat("iLoveGOOGLE", 1.61803f)
+ ->RenderFloat("goGoogleGO", 2.71828f)
+ ->RenderFloat("gBikeISCool", 3.14159f)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("GOOGLEIsTheBest")
+ ->RenderFloat("ILoveGOOGLE", 1.61803f)
+ ->RenderFloat("GOGoogleGO", 2.71828f)
+ ->RenderFloat("GBikeISCool", 3.14159f)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, MixedCase) {
+ // Sets expectation
+ expects_.StartObject("snakeCaseCamelCase")
+ ->RenderBool("camelCaseSnakeCase", false)
+ ->RenderBool("mixedCamelAndUnderScores", false)
+ ->RenderBool("goGOOGLEGo", true)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("snake_case_camelCase")
+ ->RenderBool("camelCase_snake_case", false)
+ ->RenderBool("MixedCamel_And_UnderScores", false)
+ ->RenderBool("Go_GOOGLEGo", true)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, SnakeCase) {
+ // Sets expectation
+ expects_.StartObject("")
+ ->RenderString("snakeCase", "snake_case")
+ ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog",
+ "the_quick_brown_fox_jumps_over_the_lazy_dog")
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")
+ ->RenderString("snake_case", "snake_case")
+ ->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog",
+ "the_quick_brown_fox_jumps_over_the_lazy_dog")
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, FirstCapSnakeCase) {
+ // Sets expectation
+ expects_.StartObject("firstCapSnakeCase")
+ ->RenderBool("helloWorld", true)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("First_Cap_Snake_Case")
+ ->RenderBool("Hello_World", true)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, AllCapSnakeCase) {
+ // Sets expectation
+ expects_.StartObject("allCAPSNAKECASE")
+ ->RenderDouble("nyseGOOGL", 600.0L)
+ ->RenderDouble("aBCDE", 1.0L)
+ ->RenderDouble("klMNOP", 2.0L)
+ ->RenderDouble("abcIJKPQRXYZ", 3.0L)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("ALL_CAP_SNAKE_CASE")
+ ->RenderDouble("NYSE_GOOGL", 600.0L)
+ ->RenderDouble("A_B_C_D_E", 1.0L)
+ ->RenderDouble("KL_MN_OP", 2.0L)
+ ->RenderDouble("ABC_IJK_PQR_XYZ", 3.0L)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, RepeatedUnderScoreSnakeCase) {
+ // Sets expectation
+ expects_.StartObject("")
+ ->RenderInt32("doubleUnderscoreSnakeCase", 2)
+ ->RenderInt32("tripleUnderscoreFirstCap", 3)
+ ->RenderInt32("quadrupleUNDERSCOREALLCAP", 4)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")
+ ->RenderInt32("double__underscore__snake__case", 2)
+ ->RenderInt32("Triple___Underscore___First___Cap", 3)
+ ->RenderInt32("QUADRUPLE____UNDERSCORE____ALL____CAP", 4)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, LeadingUnderscoreSnakeCase) {
+ // Sets expectation
+ expects_.StartObject("leadingUnderscoreSnakeCase")
+ ->RenderUint32("leadingDoubleUnderscore", 2)
+ ->RenderUint32("leadingTripleUnderscoreFirstCap", 3)
+ ->RenderUint32("leadingQUADRUPLEUNDERSCOREALLCAP", 4)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("_leading_underscore_snake_case")
+ ->RenderUint32("__leading_double_underscore", 2)
+ ->RenderUint32("___Leading_Triple_Underscore_First_Cap", 3)
+ ->RenderUint32("____LEADING_QUADRUPLE_UNDERSCORE_ALL_CAP", 4)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, TrailingUnderscoreSnakeCase) {
+ // Sets expectation
+ expects_.StartObject("trailingUnderscoreSnakeCase")
+ ->RenderInt64("trailingDoubleUnderscore", 2L)
+ ->RenderInt64("trailingTripleUnderscoreFirstCap", 3L)
+ ->RenderInt64("trailingQUADRUPLEUNDERSCOREALLCAP", 4L)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("trailing_underscore_snake_case")
+ ->RenderInt64("trailing_double_underscore__", 2L)
+ ->RenderInt64("Trailing_Triple_Underscore_First_Cap___", 3L)
+ ->RenderInt64("TRAILING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, EnclosingUnderscoreSnakeCase) {
+ // Sets expectation
+ expects_.StartObject("enclosingUnderscoreSnakeCase")
+ ->RenderUint64("enclosingDoubleUnderscore", 2L)
+ ->RenderUint64("enclosingTripleUnderscoreFirstCap", 3L)
+ ->RenderUint64("enclosingQUADRUPLEUNDERSCOREALLCAP", 4L)
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("_enclosing_underscore_snake_case_")
+ ->RenderUint64("__enclosing_double_underscore__", 2L)
+ ->RenderUint64("___Enclosing_Triple_Underscore_First_Cap___", 3L)
+ ->RenderUint64("____ENCLOSING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L)
+ ->EndObject();
+}
+
+TEST_F(Snake2CamelObjectWriterTest, DisableCaseNormalizationOnlyDisablesFirst) {
+ // Sets expectation
+ expects_.StartObject("")
+ ->RenderString("snakeCase", "snake_case")
+ ->RenderString(
+ "the_quick_brown_fox_jumps_over_the_lazy_dog", // case retained
+ "the_quick_brown_fox_jumps_over_the_lazy_dog")
+ ->RenderBool("theSlowFox", true) // disable case not in effect
+ ->EndObject();
+
+ // Actual testing
+ testing_.StartObject("")
+ ->RenderString("snake_case", "snake_case")
+ ->DisableCaseNormalizationForNextKey()
+ ->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog",
+ "the_quick_brown_fox_jumps_over_the_lazy_dog")
+ ->RenderBool("the_slow_fox", true)
+ ->EndObject();
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/structured_objectwriter.h b/src/google/protobuf/util/internal/structured_objectwriter.h
new file mode 100644
index 00000000..3f065d6b
--- /dev/null
+++ b/src/google/protobuf/util/internal/structured_objectwriter.h
@@ -0,0 +1,118 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/stubs/casts.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/util/internal/object_writer.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+// An StructuredObjectWriter is an ObjectWriter for writing
+// tree-structured data in a stream of events representing objects
+// and collections. Implementation of this interface can be used to
+// write an object stream to an in-memory structure, protobufs,
+// JSON, XML, or any other output format desired. The ObjectSource
+// interface is typically used as the source of an object stream.
+//
+// See JsonObjectWriter for a sample implementation of
+// StructuredObjectWriter and its use.
+//
+// Derived classes could be thread-unsafe.
+class LIBPROTOBUF_EXPORT StructuredObjectWriter : public ObjectWriter {
+ public:
+ virtual ~StructuredObjectWriter() {}
+
+ protected:
+ // A base element class for subclasses to extend, makes tracking state easier.
+ //
+ // StructuredObjectWriter behaves as a visitor. BaseElement represents a node
+ // in the input tree. Implementation of StructuredObjectWriter should also
+ // extend BaseElement to keep track of the location in the input tree.
+ class LIBPROTOBUF_EXPORT BaseElement {
+ public:
+ // Takes ownership of the parent Element.
+ explicit BaseElement(BaseElement* parent)
+ : parent_(parent), level_(parent == NULL ? 0 : parent->level() + 1) {}
+ virtual ~BaseElement() {}
+
+ // Releases ownership of the parent and returns a pointer to it.
+ template <typename ElementType>
+ ElementType* pop() {
+ return down_cast<ElementType*>(parent_.release());
+ }
+
+ // Returns true if this element is the root.
+ bool is_root() const { return parent_ == NULL; }
+
+ // Returns the number of hops from this element to the root element.
+ int level() const { return level_; }
+
+ protected:
+ // Returns pointer to parent element without releasing ownership.
+ virtual BaseElement* parent() const { return parent_.get(); }
+
+ private:
+ // Pointer to the parent Element.
+ google::protobuf::scoped_ptr<BaseElement> parent_;
+
+ // Number of hops to the root Element.
+ // The root Element has NULL parent_ and a level_ of 0.
+ const int level_;
+
+ GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(BaseElement);
+ };
+
+ StructuredObjectWriter() {}
+
+ // Returns the current element. Used for indentation and name overrides.
+ virtual BaseElement* element() = 0;
+
+ private:
+ // Do not add any data members to this class.
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StructuredObjectWriter);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__
diff --git a/src/google/protobuf/util/internal/testdata/anys.proto b/src/google/protobuf/util/internal/testdata/anys.proto
new file mode 100644
index 00000000..18c59cbb
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/anys.proto
@@ -0,0 +1,53 @@
+// 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.
+
+// Proto to test Proto3 Any serialization.
+syntax = "proto3";
+
+package google.protobuf.testing.anys;
+option java_package = "com.google.protobuf.testing.anys";
+
+import "google/protobuf/any.proto";
+
+message AnyIn {
+ string something = 1;
+}
+
+message AnyOut {
+ google.protobuf.Any any = 1;
+}
+
+message AnyM {
+ string foo = 1;
+}
+
+service TestService {
+ rpc Call(AnyIn) returns (AnyOut);
+}
diff --git a/src/google/protobuf/util/internal/testdata/books.proto b/src/google/protobuf/util/internal/testdata/books.proto
new file mode 100644
index 00000000..6e2f109b
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/books.proto
@@ -0,0 +1,171 @@
+// 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: sven@google.com (Sven Mawson)
+//
+// Sample protos for testing.
+syntax = "proto2";
+
+package google.protobuf.testing;
+
+// A book
+message Book {
+ optional string title = 1;
+ optional Author author = 2;
+ optional uint32 length = 3;
+ optional int64 published = 4;
+ optional bytes content = 5;
+
+ optional group Data = 6 {
+ optional uint32 year = 7;
+ optional string copyright = 8;
+ }
+
+ message Label {
+ optional string key = 1;
+ optional string value = 2;
+ }
+
+ optional Publisher publisher = 9;
+ repeated Label labels = 10;
+
+ extensions 200 to 499;
+}
+
+// A publisher of a book, tests required fields.
+message Publisher {
+ required string name = 1;
+}
+
+// An author of a book
+message Author {
+ optional uint64 id = 1;
+ optional string name = 2;
+ repeated string pseudonym = 3;
+ optional bool alive = 4;
+ repeated Author friend = 5;
+}
+
+// For testing resiliency of our protostream parser.
+// Field numbers of Author are reused for something else.
+message BadAuthor {
+ optional string id = 1; // non-length-delimited to length-delimited.
+ repeated uint64 name = 2; // string to repeated (both length-delimited).
+ optional string pseudonym = 3; // Repeated to optional.
+ repeated bool alive = 4 [packed=true]; // Optional to repeated.
+}
+
+// All primitive types
+message Primitive {
+ // 32 bit numbers:
+ optional fixed32 fix32 = 1;
+ optional uint32 u32 = 2;
+ optional int32 i32 = 3;
+ optional sfixed32 sf32 = 4;
+ optional sint32 s32 = 5;
+
+ // 64 bit numbers:
+ optional fixed64 fix64 = 6;
+ optional uint64 u64 = 7;
+ optional int64 i64 = 8;
+ optional sfixed64 sf64 = 9;
+ optional sint64 s64 = 10;
+
+ // The other stuff.
+ optional string str = 11;
+ optional bytes bytes = 12;
+ optional float float = 13;
+ optional double double = 14;
+ optional bool bool = 15;
+
+ // repeated 32 bit numbers:
+ repeated fixed32 rep_fix32 = 16;
+ repeated uint32 rep_u32 = 17;
+ repeated int32 rep_i32 = 18;
+ repeated sfixed32 rep_sf32 = 19;
+ repeated sint32 rep_s32 = 20;
+
+ // repeated 64 bit numbers:
+ repeated fixed64 rep_fix64 = 21;
+ repeated uint64 rep_u64 = 22;
+ repeated int64 rep_i64 = 23;
+ repeated sfixed64 rep_sf64 = 24;
+ repeated sint64 rep_s64 = 25;
+
+ // repeated other stuff:
+ repeated string rep_str = 26;
+ repeated bytes rep_bytes = 27;
+ repeated float rep_float = 28;
+ repeated double rep_double = 29;
+ repeated bool rep_bool = 30;
+}
+
+// Test packed versions of all repeated primitives.
+// The field numbers should match their non-packed version in Primitive message.
+message PackedPrimitive {
+ // repeated 32 bit numbers:
+ repeated fixed32 rep_fix32 = 16 [packed=true];
+ repeated uint32 rep_u32 = 17 [packed=true];
+ repeated int32 rep_i32 = 18 [packed=true];
+ repeated sfixed32 rep_sf32 = 19 [packed=true];
+ repeated sint32 rep_s32 = 20 [packed=true];
+
+ // repeated 64 bit numbers:
+ repeated fixed64 rep_fix64 = 21 [packed=true];
+ repeated uint64 rep_u64 = 22 [packed=true];
+ repeated int64 rep_i64 = 23 [packed=true];
+ repeated sfixed64 rep_sf64 = 24 [packed=true];
+ repeated sint64 rep_s64 = 25 [packed=true];
+
+ // repeated other stuff:
+ repeated float rep_float = 28 [packed=true];
+ repeated double rep_double = 29 [packed=true];
+ repeated bool rep_bool = 30 [packed=true];
+}
+
+// Test extensions.
+extend Book {
+ repeated Author more_author = 201;
+}
+
+// Test nested extensions.
+message NestedBook {
+ extend Book {
+ optional NestedBook another_book = 301;
+ }
+ // Recurse
+ optional Book book = 1;
+}
+
+// For testing resiliency of our protostream parser.
+// Field number of NestedBook is reused for something else.
+message BadNestedBook {
+ repeated uint32 book = 1 [packed=true]; // Packed to optional message.
+}
diff --git a/src/google/protobuf/util/internal/testdata/default_value.proto b/src/google/protobuf/util/internal/testdata/default_value.proto
new file mode 100644
index 00000000..ecfc8119
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/default_value.proto
@@ -0,0 +1,162 @@
+// 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.testing;
+
+import "google/protobuf/any.proto";
+import "google/protobuf/struct.proto";
+import "google/protobuf/wrappers.proto";
+
+message DefaultValueTestCases {
+ DoubleMessage empty_double = 1;
+ DoubleMessage double_with_default_value = 2;
+ DoubleMessage double_with_nondefault_value = 3;
+ DoubleMessage repeated_double = 4;
+ DoubleMessage nested_message = 5;
+ DoubleMessage repeated_nested_message = 6;
+ StructMessage empty_struct = 201;
+ StructMessage empty_struct2 = 202;
+ StructMessage struct_with_null_value = 203;
+ StructMessage struct_with_values = 204;
+ StructMessage struct_with_nested_struct = 205;
+ StructMessage struct_with_nested_list = 206;
+ StructMessage struct_with_list_of_nulls = 207;
+ StructMessage struct_with_list_of_lists = 208;
+ StructMessage struct_with_list_of_structs = 209;
+ google.protobuf.Struct top_level_struct = 210;
+ ValueMessage value_wrapper_simple = 212;
+ ValueMessage value_wrapper_with_struct = 213;
+ ValueMessage value_wrapper_with_list = 214;
+ ListValueMessage list_value_wrapper = 215;
+ google.protobuf.Value top_level_value_simple = 216;
+ google.protobuf.Value top_level_value_with_struct = 217;
+ google.protobuf.Value top_level_value_with_list = 218;
+ google.protobuf.ListValue top_level_listvalue = 219;
+ AnyMessage empty_any = 301;
+ AnyMessage type_only_any = 302;
+ AnyMessage recursive_any = 303;
+ AnyMessage any_with_message_value = 304;
+ AnyMessage any_with_nested_message = 305;
+ AnyMessage any_with_message_containing_map = 306;
+ AnyMessage any_with_message_containing_struct = 307;
+ google.protobuf.Any top_level_any = 308;
+ StringtoIntMap empty_map = 401;
+ StringtoIntMap string_to_int = 402;
+ IntToStringMap int_to_string = 403;
+ MixedMap mixed1 = 404;
+ MixedMap2 mixed2 = 405;
+ MessageMap map_of_objects = 406;
+ DoubleValueMessage double_value = 501;
+ DoubleValueMessage double_value_default = 502;
+}
+
+message DoubleMessage {
+ double double_value = 1;
+ repeated double repeated_double = 2;
+ DoubleMessage nested_message = 3;
+ repeated DoubleMessage repeated_nested_message = 4;
+ google.protobuf.DoubleValue double_wrapper = 100;
+}
+
+message StructMessage {
+ google.protobuf.Struct struct = 1;
+}
+
+message ValueMessage {
+ google.protobuf.Value value = 1;
+}
+
+message ListValueMessage {
+ google.protobuf.ListValue shopping_list = 1;
+}
+message RequestMessage {
+ string content = 1;
+}
+
+// A test service.
+service DefaultValueTestService {
+ // A test method.
+ rpc Call(RequestMessage) returns (DefaultValueTestCases);
+}
+
+message AnyMessage {
+ google.protobuf.Any any = 1;
+ AnyData data = 2;
+}
+
+message AnyData {
+ int32 attr = 1;
+ string str = 2;
+ repeated string msgs = 3;
+ AnyData nested_data = 4;
+ map<string, string> map_data = 7;
+ google.protobuf.Struct struct_data = 8;
+ repeated AnyData repeated_data = 9;
+}
+
+message StringtoIntMap {
+ map<string, int32> map = 1;
+}
+
+message IntToStringMap {
+ map<int32, string> map = 1;
+}
+
+message MixedMap {
+ string msg = 1;
+ map<string, float> map = 2;
+ int32 int_value = 3;
+}
+
+message MixedMap2 {
+ enum E {
+ E0 = 0;
+ E1 = 1;
+ E2 = 2;
+ E3 = 3;
+ }
+ map<int32, bool> map = 1;
+ E ee = 2;
+ string msg = 4;
+}
+
+message MessageMap {
+ message M {
+ int32 inner_int = 1;
+ string inner_text = 2;
+ }
+ map<string, M> map = 1;
+}
+
+message DoubleValueMessage {
+ google.protobuf.DoubleValue double = 1;
+}
diff --git a/src/google/protobuf/util/internal/testdata/default_value_test.proto b/src/google/protobuf/util/internal/testdata/default_value_test.proto
new file mode 100644
index 00000000..21b85e6d
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/default_value_test.proto
@@ -0,0 +1,46 @@
+// 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.testing;
+
+message DefaultValueTest {
+ double double_value = 1;
+ repeated double repeated_double = 2;
+ float float_value = 3;
+ int64 int64_value = 5;
+ uint64 uint64_value = 7;
+ int32 int32_value = 9;
+ uint32 uint32_value = 11;
+ bool bool_value = 13;
+ string string_value = 15;
+ bytes bytes_value = 17 [ctype = CORD];
+}
diff --git a/src/google/protobuf/util/internal/testdata/field_mask.proto b/src/google/protobuf/util/internal/testdata/field_mask.proto
new file mode 100644
index 00000000..e8b2bc5f
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/field_mask.proto
@@ -0,0 +1,71 @@
+// 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.testing;
+
+import "google/protobuf/field_mask.proto";
+
+message NestedFieldMask {
+ string data = 1;
+ google.protobuf.FieldMask single_mask = 2;
+ repeated google.protobuf.FieldMask repeated_mask = 3;
+}
+
+message FieldMaskTest {
+ string id = 1;
+ google.protobuf.FieldMask single_mask = 2;
+ repeated google.protobuf.FieldMask repeated_mask = 3;
+ repeated NestedFieldMask nested_mask = 4;
+}
+
+message FieldMaskTestCases {
+ FieldMaskWrapper single_mask = 1;
+ FieldMaskWrapper multiple_mask = 2;
+ FieldMaskWrapper snake_camel = 3;
+ FieldMaskWrapper empty_field = 4;
+ FieldMaskWrapper apiary_format1 = 5;
+ FieldMaskWrapper apiary_format2 = 6;
+ FieldMaskWrapper apiary_format3 = 7;
+ FieldMaskWrapper map_key1 = 8;
+ FieldMaskWrapper map_key2 = 9;
+ FieldMaskWrapper map_key3 = 10;
+ FieldMaskWrapper map_key4 = 11;
+ FieldMaskWrapper map_key5 = 12;
+}
+
+message FieldMaskWrapper {
+ google.protobuf.FieldMask mask = 1;
+}
+
+service FieldMaskTestService {
+ rpc Call(FieldMaskTestCases) returns (FieldMaskTestCases);
+}
diff --git a/src/google/protobuf/util/internal/testdata/maps.proto b/src/google/protobuf/util/internal/testdata/maps.proto
new file mode 100644
index 00000000..7fb42a26
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/maps.proto
@@ -0,0 +1,57 @@
+// 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.
+
+// Proto to test proto3 maps.
+syntax = "proto3";
+
+package google.protobuf.testing.maps;
+option java_package = "com.google.protobuf.testing.maps";
+
+message MapIn {
+ string other = 1;
+ repeated string things = 2;
+ map<string, string> map_input = 3;
+}
+
+message MapOut {
+ map<string, MapM> map1 = 1;
+ map<string, MapOut> map2 = 2;
+ map<int32, string> map3 = 3;
+ string bar = 4;
+}
+
+message MapM {
+ string foo = 1;
+}
+
+service TestService {
+ rpc Call1(MapIn) returns (MapOut);
+ rpc Call2(MapIn) returns (MapOut);
+}
diff --git a/src/google/protobuf/util/internal/testdata/struct.proto b/src/google/protobuf/util/internal/testdata/struct.proto
new file mode 100644
index 00000000..c15aba0d
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/struct.proto
@@ -0,0 +1,45 @@
+// 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.
+
+// Proto to test proto3 struct.
+syntax = "proto3";
+
+package google.protobuf.testing.structs;
+option java_package = "com.google.protobuf.testing.structs";
+
+import "google/protobuf/struct.proto";
+
+message StructType {
+ google.protobuf.Struct object = 1;
+}
+
+service TestService {
+ rpc Call(StructType) returns (StructType);
+}
diff --git a/src/google/protobuf/util/internal/testdata/timestamp_duration.proto b/src/google/protobuf/util/internal/testdata/timestamp_duration.proto
new file mode 100644
index 00000000..56351f16
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/timestamp_duration.proto
@@ -0,0 +1,47 @@
+// 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.
+
+// Proto to test proto3 Timestamp and Duration.
+syntax = "proto3";
+
+package google.protobuf.testing.timestampduration;
+option java_package = "com.google.protobuf.testing.timestampduration";
+
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/duration.proto";
+
+message TimestampDuration {
+ google.protobuf.Timestamp ts = 1;
+ google.protobuf.Duration dur = 2;
+}
+
+service TestService {
+ rpc Call(TimestampDuration) returns (TimestampDuration);
+}
diff --git a/src/google/protobuf/util/internal/testdata/wrappers.proto b/src/google/protobuf/util/internal/testdata/wrappers.proto
new file mode 100644
index 00000000..eabc99f2
--- /dev/null
+++ b/src/google/protobuf/util/internal/testdata/wrappers.proto
@@ -0,0 +1,100 @@
+// 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.testing;
+
+import "google/protobuf/wrappers.proto";
+
+// Top-level test cases proto used by MarshallingTest. See description
+// at the top of the class MarshallingTest for details on how to write
+// test cases.
+message WrappersTestCases {
+ DoubleWrapper double_wrapper = 1;
+ FloatWrapper float_wrapper = 2;
+ Int64Wrapper int64_wrapper = 3;
+ UInt64Wrapper uint64_wrapper = 4;
+ Int32Wrapper int32_wrapper = 5;
+ UInt32Wrapper uint32_wrapper = 6;
+ BoolWrapper bool_wrapper = 7;
+ StringWrapper string_wrapper = 8;
+ BytesWrapper bytes_wrapper = 9;
+
+ DoubleWrapper double_wrapper_default = 10;
+ FloatWrapper float_wrapper_default = 11;
+ Int64Wrapper int64_wrapper_default = 12;
+ UInt64Wrapper uint64_wrapper_default = 13;
+ Int32Wrapper int32_wrapper_default = 14;
+ UInt32Wrapper uint32_wrapper_default = 15;
+ BoolWrapper bool_wrapper_default = 16;
+ StringWrapper string_wrapper_default = 17;
+ BytesWrapper bytes_wrapper_default = 18;
+}
+
+message DoubleWrapper {
+ google.protobuf.DoubleValue double = 1;
+}
+
+message FloatWrapper {
+ google.protobuf.FloatValue float = 1;
+}
+
+message Int64Wrapper {
+ google.protobuf.Int64Value int64 = 1;
+}
+
+message UInt64Wrapper {
+ google.protobuf.UInt64Value uint64 = 1;
+}
+
+message Int32Wrapper {
+ google.protobuf.Int32Value int32 = 1;
+}
+
+message UInt32Wrapper {
+ google.protobuf.UInt32Value uint32 = 1;
+}
+
+message BoolWrapper {
+ google.protobuf.BoolValue bool = 1;
+}
+
+message StringWrapper {
+ google.protobuf.StringValue string = 1;
+}
+
+message BytesWrapper {
+ google.protobuf.BytesValue bytes = 1;
+}
+
+service WrappersTestService {
+ rpc Call(WrappersTestCases) returns (WrappersTestCases);
+}
diff --git a/src/google/protobuf/util/internal/type_info.cc b/src/google/protobuf/util/internal/type_info.cc
new file mode 100644
index 00000000..6392e18c
--- /dev/null
+++ b/src/google/protobuf/util/internal/type_info.cc
@@ -0,0 +1,171 @@
+// 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 <google/protobuf/util/internal/type_info.h>
+
+#include <map>
+#include <set>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/map_util.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+#include <google/protobuf/util/internal/utility.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+
+// A TypeInfo that looks up information provided by a TypeResolver.
+class TypeInfoForTypeResolver : public TypeInfo {
+ public:
+ explicit TypeInfoForTypeResolver(TypeResolver* type_resolver)
+ : type_resolver_(type_resolver) {}
+
+ virtual ~TypeInfoForTypeResolver() {
+ DeleteCachedTypes(&cached_types_);
+ DeleteCachedTypes(&cached_enums_);
+ }
+
+ virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl(
+ StringPiece type_url) {
+ map<StringPiece, StatusOrType>::iterator it = cached_types_.find(type_url);
+ if (it != cached_types_.end()) {
+ return it->second;
+ }
+ // Stores the string value so it can be referenced using StringPiece in the
+ // cached_types_ map.
+ const string& string_type_url =
+ *string_storage_.insert(type_url.ToString()).first;
+ google::protobuf::scoped_ptr<google::protobuf::Type> type(new google::protobuf::Type());
+ util::Status status =
+ type_resolver_->ResolveMessageType(string_type_url, type.get());
+ StatusOrType result =
+ status.ok() ? StatusOrType(type.release()) : StatusOrType(status);
+ cached_types_[string_type_url] = result;
+ return result;
+ }
+
+ virtual const google::protobuf::Type* GetType(StringPiece type_url) {
+ StatusOrType result = ResolveTypeUrl(type_url);
+ return result.ok() ? result.ValueOrDie() : NULL;
+ }
+
+ virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) {
+ map<StringPiece, StatusOrEnum>::iterator it = cached_enums_.find(type_url);
+ if (it != cached_enums_.end()) {
+ return it->second.ok() ? it->second.ValueOrDie() : NULL;
+ }
+ // Stores the string value so it can be referenced using StringPiece in the
+ // cached_enums_ map.
+ const string& string_type_url =
+ *string_storage_.insert(type_url.ToString()).first;
+ google::protobuf::scoped_ptr<google::protobuf::Enum> enum_type(
+ new google::protobuf::Enum());
+ util::Status status =
+ type_resolver_->ResolveEnumType(string_type_url, enum_type.get());
+ StatusOrEnum result =
+ status.ok() ? StatusOrEnum(enum_type.release()) : StatusOrEnum(status);
+ cached_enums_[string_type_url] = result;
+ return result.ok() ? result.ValueOrDie() : NULL;
+ }
+
+ virtual const google::protobuf::Field* FindField(
+ const google::protobuf::Type* type, StringPiece camel_case_name) {
+ if (indexed_types_.find(type) == indexed_types_.end()) {
+ PopulateNameLookupTable(type);
+ indexed_types_.insert(type);
+ }
+ StringPiece name =
+ FindWithDefault(camel_case_name_table_, camel_case_name, StringPiece());
+ if (name.empty()) {
+ // Didn't find a mapping. Use whatever provided.
+ name = camel_case_name;
+ }
+ return FindFieldInTypeOrNull(type, name);
+ }
+
+ private:
+ typedef util::StatusOr<const google::protobuf::Type*> StatusOrType;
+ typedef util::StatusOr<const google::protobuf::Enum*> StatusOrEnum;
+
+ template <typename T>
+ static void DeleteCachedTypes(map<StringPiece, T>* cached_types) {
+ for (typename map<StringPiece, T>::iterator it = cached_types->begin();
+ it != cached_types->end(); ++it) {
+ if (it->second.ok()) {
+ delete it->second.ValueOrDie();
+ }
+ }
+ }
+
+ void PopulateNameLookupTable(const google::protobuf::Type* type) {
+ for (int i = 0; i < type->fields_size(); ++i) {
+ const google::protobuf::Field& field = type->fields(i);
+ StringPiece name = field.name();
+ StringPiece camel_case_name =
+ *string_storage_.insert(ToCamelCase(name)).first;
+ const StringPiece* existing = InsertOrReturnExisting(
+ &camel_case_name_table_, camel_case_name, name);
+ if (existing && *existing != name) {
+ GOOGLE_LOG(WARNING) << "Field '" << name << "' and '" << *existing
+ << "' map to the same camel case name '" << camel_case_name
+ << "'.";
+ }
+ }
+ }
+
+ TypeResolver* type_resolver_;
+
+ // Stores string values that will be referenced by StringPieces in
+ // cached_types_, cached_enums_ and camel_case_name_table_.
+ set<string> string_storage_;
+
+ map<StringPiece, StatusOrType> cached_types_;
+ map<StringPiece, StatusOrEnum> cached_enums_;
+
+ set<const google::protobuf::Type*> indexed_types_;
+ map<StringPiece, StringPiece> camel_case_name_table_;
+};
+} // namespace
+
+TypeInfo* TypeInfo::NewTypeInfo(TypeResolver* type_resolver) {
+ return new TypeInfoForTypeResolver(type_resolver);
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/type_info.h b/src/google/protobuf/util/internal/type_info.h
new file mode 100644
index 00000000..67403fff
--- /dev/null
+++ b/src/google/protobuf/util/internal/type_info.h
@@ -0,0 +1,87 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_H__
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+// Internal helper class for type resolving. Note that this class is not
+// thread-safe and should only be accessed in one thread.
+class LIBPROTOBUF_EXPORT TypeInfo {
+ public:
+ TypeInfo() {}
+ virtual ~TypeInfo() {}
+
+ // Resolves a type url into a Type. If the type url is invalid, returns
+ // INVALID_ARGUMENT error status. If the type url is valid but the
+ // corresponding type cannot be found, returns a NOT_FOUND error status.
+ //
+ // This TypeInfo class retains the ownership of the returned pointer.
+ virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl(
+ StringPiece type_url) = 0;
+
+ // Resolves a type url into a Type. Like ResolveTypeUrl() but returns
+ // NULL if the type url is invalid or the type cannot be found.
+ //
+ // This TypeInfo class retains the ownership of the returned pointer.
+ virtual const google::protobuf::Type* GetType(StringPiece type_url) = 0;
+
+ // Resolves a type url for an enum. Returns NULL if the type url is
+ // invalid or the type cannot be found.
+ //
+ // This TypeInfo class retains the ownership of the returned pointer.
+ virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) = 0;
+
+ // Looks up a field in the specified type given a CamelCase name.
+ virtual const google::protobuf::Field* FindField(
+ const google::protobuf::Type* type, StringPiece camel_case_name) = 0;
+
+ static TypeInfo* NewTypeInfo(TypeResolver* type_resolver);
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeInfo);
+};
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_H__
diff --git a/src/google/protobuf/util/internal/type_info_test_helper.cc b/src/google/protobuf/util/internal/type_info_test_helper.cc
new file mode 100644
index 00000000..f7aea857
--- /dev/null
+++ b/src/google/protobuf/util/internal/type_info_test_helper.cc
@@ -0,0 +1,130 @@
+// 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 <google/protobuf/util/internal/type_info_test_helper.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <vector>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/default_value_objectwriter.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/util/internal/protostream_objectsource.h>
+#include <google/protobuf/util/internal/protostream_objectwriter.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/util/type_resolver_util.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+namespace testing {
+
+
+void TypeInfoTestHelper::ResetTypeInfo(
+ const vector<const Descriptor*>& descriptors) {
+ switch (type_) {
+ case USE_TYPE_RESOLVER: {
+ const DescriptorPool* pool = descriptors[0]->file()->pool();
+ for (int i = 1; i < descriptors.size(); ++i) {
+ GOOGLE_CHECK(pool == descriptors[i]->file()->pool())
+ << "Descriptors from different pools are not supported.";
+ }
+ type_resolver_.reset(
+ NewTypeResolverForDescriptorPool(kTypeServiceBaseUrl, pool));
+ typeinfo_.reset(TypeInfo::NewTypeInfo(type_resolver_.get()));
+ return;
+ }
+ }
+ GOOGLE_LOG(FATAL) << "Can not reach here.";
+}
+
+void TypeInfoTestHelper::ResetTypeInfo(const Descriptor* descriptor) {
+ vector<const Descriptor*> descriptors;
+ descriptors.push_back(descriptor);
+ ResetTypeInfo(descriptors);
+}
+
+void TypeInfoTestHelper::ResetTypeInfo(const Descriptor* descriptor1,
+ const Descriptor* descriptor2) {
+ vector<const Descriptor*> descriptors;
+ descriptors.push_back(descriptor1);
+ descriptors.push_back(descriptor2);
+ ResetTypeInfo(descriptors);
+}
+
+TypeInfo* TypeInfoTestHelper::GetTypeInfo() { return typeinfo_.get(); }
+
+ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource(
+ io::CodedInputStream* coded_input, const string& type_url) {
+ const google::protobuf::Type* type = typeinfo_->GetType(type_url);
+ switch (type_) {
+ case USE_TYPE_RESOLVER: {
+ return new ProtoStreamObjectSource(coded_input, type_resolver_.get(),
+ *type);
+ }
+ }
+ GOOGLE_LOG(FATAL) << "Can not reach here.";
+}
+
+ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter(
+ const string& type_url, strings::ByteSink* output,
+ ErrorListener* listener) {
+ const google::protobuf::Type* type = typeinfo_->GetType(type_url);
+ switch (type_) {
+ case USE_TYPE_RESOLVER: {
+ return new ProtoStreamObjectWriter(type_resolver_.get(), *type, output,
+ listener);
+ }
+ }
+ GOOGLE_LOG(FATAL) << "Can not reach here.";
+}
+
+DefaultValueObjectWriter* TypeInfoTestHelper::NewDefaultValueWriter(
+ const string& type_url, ObjectWriter* writer) {
+ const google::protobuf::Type* type = typeinfo_->GetType(type_url);
+ switch (type_) {
+ case USE_TYPE_RESOLVER: {
+ return new DefaultValueObjectWriter(type_resolver_.get(), *type, writer);
+ }
+ }
+ GOOGLE_LOG(FATAL) << "Can not reach here.";
+}
+
+} // namespace testing
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/type_info_test_helper.h b/src/google/protobuf/util/internal/type_info_test_helper.h
new file mode 100644
index 00000000..6916a73b
--- /dev/null
+++ b/src/google/protobuf/util/internal/type_info_test_helper.h
@@ -0,0 +1,98 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_TEST_HELPER_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_TEST_HELPER_H__
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <vector>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/type_info.h>
+#include <google/protobuf/util/internal/default_value_objectwriter.h>
+#include <google/protobuf/util/internal/protostream_objectsource.h>
+#include <google/protobuf/util/internal/protostream_objectwriter.h>
+#include <google/protobuf/util/type_resolver.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+namespace testing {
+
+enum TypeInfoSource {
+ USE_TYPE_RESOLVER,
+};
+
+// In the unit-tests we want to test two scenarios: one with type info from
+// ServiceTypeInfo, the other with type info from TypeResolver. This class
+// wraps the detail of where the type info is from and provides the same
+// interface so the same unit-test code can test both scenarios.
+class TypeInfoTestHelper {
+ public:
+ explicit TypeInfoTestHelper(TypeInfoSource type) : type_(type) {}
+
+ // Creates a TypeInfo object for the given set of descriptors.
+ void ResetTypeInfo(const vector<const Descriptor*>& descriptors);
+
+ // Convinent overloads.
+ void ResetTypeInfo(const Descriptor* descriptor);
+ void ResetTypeInfo(const Descriptor* descriptor1,
+ const Descriptor* descriptor2);
+
+ // Returns the TypeInfo created after ResetTypeInfo.
+ TypeInfo* GetTypeInfo();
+
+ ProtoStreamObjectSource* NewProtoSource(io::CodedInputStream* coded_input,
+ const string& type_url);
+
+ ProtoStreamObjectWriter* NewProtoWriter(const string& type_url,
+ strings::ByteSink* output,
+ ErrorListener* listener);
+
+ DefaultValueObjectWriter* NewDefaultValueWriter(const string& type_url,
+ ObjectWriter* writer);
+
+ private:
+ TypeInfoSource type_;
+ google::protobuf::scoped_ptr<TypeInfo> typeinfo_;
+ google::protobuf::scoped_ptr<TypeResolver> type_resolver_;
+};
+} // namespace testing
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_TEST_HELPER_H__
diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc
new file mode 100644
index 00000000..b6ec19b6
--- /dev/null
+++ b/src/google/protobuf/util/internal/utility.cc
@@ -0,0 +1,332 @@
+// 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 <google/protobuf/util/internal/utility.h>
+
+#include <cmath>
+#include <algorithm>
+#include <utility>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/wrappers.pb.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/internal/constants.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/map_util.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace converter {
+
+namespace {
+const StringPiece SkipWhiteSpace(StringPiece str) {
+ StringPiece::size_type i;
+ for (i = 0; i < str.size() && isspace(str[i]); ++i) {}
+ GOOGLE_DCHECK(i == str.size() || !isspace(str[i]));
+ return StringPiece(str, i);
+}
+} // namespace
+
+bool GetBoolOptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, bool default_value) {
+ const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+ if (opt == NULL) {
+ return default_value;
+ }
+ return GetBoolFromAny(opt->value());
+}
+
+int64 GetInt64OptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, int64 default_value) {
+ const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+ if (opt == NULL) {
+ return default_value;
+ }
+ return GetInt64FromAny(opt->value());
+}
+
+double GetDoubleOptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, double default_value) {
+ const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+ if (opt == NULL) {
+ return default_value;
+ }
+ return GetDoubleFromAny(opt->value());
+}
+
+string GetStringOptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, const string& default_value) {
+ const google::protobuf::Option* opt = FindOptionOrNull(options, option_name);
+ if (opt == NULL) {
+ return default_value;
+ }
+ return GetStringFromAny(opt->value());
+}
+
+template <typename T>
+void ParseFromAny(const string& data, T* result) {
+ result->ParseFromString(data);
+}
+
+// Returns a boolean value contained in Any type.
+// TODO(skarvaje): Add type checking & error messages here.
+bool GetBoolFromAny(const google::protobuf::Any& any) {
+ google::protobuf::BoolValue b;
+ ParseFromAny(any.value(), &b);
+ return b.value();
+}
+
+int64 GetInt64FromAny(const google::protobuf::Any& any) {
+ google::protobuf::Int64Value i;
+ ParseFromAny(any.value(), &i);
+ return i.value();
+}
+
+double GetDoubleFromAny(const google::protobuf::Any& any) {
+ google::protobuf::DoubleValue i;
+ ParseFromAny(any.value(), &i);
+ return i.value();
+}
+
+string GetStringFromAny(const google::protobuf::Any& any) {
+ google::protobuf::StringValue s;
+ ParseFromAny(any.value(), &s);
+ return s.value();
+}
+
+const StringPiece GetTypeWithoutUrl(StringPiece type_url) {
+ size_t idx = type_url.rfind('/');
+ return type_url.substr(idx + 1);
+}
+
+const string GetFullTypeWithUrl(StringPiece simple_type) {
+ return StrCat(kTypeServiceBaseUrl, "/", simple_type);
+}
+
+const google::protobuf::Option* FindOptionOrNull(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name) {
+ for (int i = 0; i < options.size(); ++i) {
+ const google::protobuf::Option& opt = options.Get(i);
+ if (opt.name() == option_name) {
+ return &opt;
+ }
+ }
+ return NULL;
+}
+
+const google::protobuf::Field* FindFieldInTypeOrNull(
+ const google::protobuf::Type* type, StringPiece field_name) {
+ if (type != NULL) {
+ for (int i = 0; i < type->fields_size(); ++i) {
+ const google::protobuf::Field& field = type->fields(i);
+ if (field.name() == field_name) {
+ return &field;
+ }
+ }
+ }
+ return NULL;
+}
+
+const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
+ const google::protobuf::Enum* enum_type, StringPiece enum_name) {
+ if (enum_type != NULL) {
+ for (int i = 0; i < enum_type->enumvalue_size(); ++i) {
+ const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(i);
+ if (enum_value.name() == enum_name) {
+ return &enum_value;
+ }
+ }
+ }
+ return NULL;
+}
+
+const google::protobuf::EnumValue* FindEnumValueByNumberOrNull(
+ const google::protobuf::Enum* enum_type, int32 value) {
+ if (enum_type != NULL) {
+ for (int i = 0; i < enum_type->enumvalue_size(); ++i) {
+ const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(i);
+ if (enum_value.number() == value) {
+ return &enum_value;
+ }
+ }
+ }
+ return NULL;
+}
+
+string ToCamelCase(const StringPiece input) {
+ bool capitalize_next = false;
+ bool was_cap = true;
+ bool is_cap = false;
+ bool first_word = true;
+ string result;
+ result.reserve(input.size());
+
+ for (size_t i = 0; i < input.size(); ++i, was_cap = is_cap) {
+ is_cap = ascii_isupper(input[i]);
+ if (input[i] == '_') {
+ capitalize_next = true;
+ if (!result.empty()) first_word = false;
+ continue;
+ } else if (first_word) {
+ // Consider when the current character B is capitalized,
+ // first word ends when:
+ // 1) following a lowercase: "...aB..."
+ // 2) followed by a lowercase: "...ABc..."
+ if (!result.empty() && is_cap &&
+ (!was_cap || (i + 1 < input.size() && ascii_islower(input[i + 1])))) {
+ first_word = false;
+ } else {
+ result.push_back(ascii_tolower(input[i]));
+ continue;
+ }
+ } else if (capitalize_next) {
+ capitalize_next = false;
+ if (ascii_islower(input[i])) {
+ result.push_back(ascii_toupper(input[i]));
+ continue;
+ }
+ }
+ result.push_back(input[i]);
+ }
+ return result;
+}
+
+string ToSnakeCase(StringPiece input) {
+ bool was_not_underscore = false; // Initialize to false for case 1 (below)
+ bool was_not_cap = false;
+ string result;
+ result.reserve(input.size() << 1);
+
+ for (size_t i = 0; i < input.size(); ++i) {
+ if (ascii_isupper(input[i])) {
+ // Consider when the current character B is capitalized:
+ // 1) At beginning of input: "B..." => "b..."
+ // (e.g. "Biscuit" => "biscuit")
+ // 2) Following a lowercase: "...aB..." => "...a_b..."
+ // (e.g. "gBike" => "g_bike")
+ // 3) At the end of input: "...AB" => "...ab"
+ // (e.g. "GoogleLAB" => "google_lab")
+ // 4) Followed by a lowercase: "...ABc..." => "...a_bc..."
+ // (e.g. "GBike" => "g_bike")
+ if (was_not_underscore && // case 1 out
+ (was_not_cap || // case 2 in, case 3 out
+ (i + 1 < input.size() && // case 3 out
+ ascii_islower(input[i + 1])))) { // case 4 in
+ // We add an underscore for case 2 and case 4.
+ result.push_back('_');
+ }
+ result.push_back(ascii_tolower(input[i]));
+ was_not_underscore = true;
+ was_not_cap = false;
+ } else {
+ result.push_back(input[i]);
+ was_not_underscore = input[i] != '_';
+ was_not_cap = true;
+ }
+ }
+ return result;
+}
+
+set<string>* well_known_types_ = NULL;
+GOOGLE_PROTOBUF_DECLARE_ONCE(well_known_types_init_);
+const char* well_known_types_name_array_[] = {
+ "google.protobuf.Timestamp", "google.protobuf.Duration",
+ "google.protobuf.DoubleValue", "google.protobuf.FloatValue",
+ "google.protobuf.Int64Value", "google.protobuf.UInt64Value",
+ "google.protobuf.Int32Value", "google.protobuf.UInt32Value",
+ "google.protobuf.BoolValue", "google.protobuf.StringValue",
+ "google.protobuf.BytesValue", "google.protobuf.FieldMask"};
+
+void DeleteWellKnownTypes() { delete well_known_types_; }
+
+void InitWellKnownTypes() {
+ well_known_types_ = new set<string>;
+ for (int i = 0; i < GOOGLE_ARRAYSIZE(well_known_types_name_array_); ++i) {
+ well_known_types_->insert(well_known_types_name_array_[i]);
+ }
+ google::protobuf::internal::OnShutdown(&DeleteWellKnownTypes);
+}
+
+bool IsWellKnownType(const string& type_name) {
+ InitWellKnownTypes();
+ return ContainsKey(*well_known_types_, type_name);
+}
+
+bool IsValidBoolString(const string& bool_string) {
+ return bool_string == "true" || bool_string == "false" ||
+ bool_string == "1" || bool_string == "0";
+}
+
+bool IsMap(const google::protobuf::Field& field,
+ const google::protobuf::Type& type) {
+ return (field.cardinality() ==
+ google::protobuf::Field_Cardinality_CARDINALITY_REPEATED &&
+ GetBoolOptionOrDefault(type.options(),
+ "google.protobuf.MessageOptions.map_entry", false));
+}
+
+string DoubleAsString(double value) {
+ if (value == std::numeric_limits<double>::infinity()) return "Infinity";
+ if (value == -std::numeric_limits<double>::infinity()) return "-Infinity";
+ if (isnan(value)) return "NaN";
+
+ return SimpleDtoa(value);
+}
+
+string FloatAsString(float value) {
+ if (isfinite(value)) return SimpleFtoa(value);
+ return DoubleAsString(value);
+}
+
+bool SafeStrToFloat(StringPiece str, float *value) {
+ double double_value;
+ if (!safe_strtod(str, &double_value)) {
+ return false;
+ }
+ *value = static_cast<float>(double_value);
+
+ if ((*value == numeric_limits<float>::infinity()) ||
+ (*value == -numeric_limits<float>::infinity())) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/internal/utility.h b/src/google/protobuf/util/internal/utility.h
new file mode 100644
index 00000000..d0d88c19
--- /dev/null
+++ b/src/google/protobuf/util/internal/utility.h
@@ -0,0 +1,187 @@
+// 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 GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__
+#define GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/repeated_field.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+
+
+namespace google {
+namespace protobuf {
+class Method;
+class Any;
+class Bool;
+class Option;
+class Field;
+class Type;
+class Enum;
+class EnumValue;
+} // namespace protobuf
+
+
+namespace protobuf {
+namespace util {
+namespace converter {
+// Finds the tech option identified by option_name. Parses the boolean value and
+// returns it.
+// When the option with the given name is not found, default_value is returned.
+LIBPROTOBUF_EXPORT bool GetBoolOptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, bool default_value);
+
+// Returns int64 option value. If the option isn't found, returns the
+// default_value.
+LIBPROTOBUF_EXPORT int64 GetInt64OptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, int64 default_value);
+
+// Returns double option value. If the option isn't found, returns the
+// default_value.
+LIBPROTOBUF_EXPORT double GetDoubleOptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, double default_value);
+
+// Returns string option value. If the option isn't found, returns the
+// default_value.
+LIBPROTOBUF_EXPORT string GetStringOptionOrDefault(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name, const string& default_value);
+
+// Returns a boolean value contained in Any type.
+// TODO(skarvaje): Make these utilities dealing with Any types more generic,
+// add more error checking and move to a more public/sharable location so others
+// can use.
+LIBPROTOBUF_EXPORT bool GetBoolFromAny(const google::protobuf::Any& any);
+
+// Returns int64 value contained in Any type.
+LIBPROTOBUF_EXPORT int64 GetInt64FromAny(const google::protobuf::Any& any);
+
+// Returns double value contained in Any type.
+LIBPROTOBUF_EXPORT double GetDoubleFromAny(const google::protobuf::Any& any);
+
+// Returns string value contained in Any type.
+LIBPROTOBUF_EXPORT string GetStringFromAny(const google::protobuf::Any& any);
+
+// Returns the type string without the url prefix. e.g.: If the passed type is
+// 'type.googleapis.com/tech.type.Bool', the returned value is 'tech.type.Bool'.
+LIBPROTOBUF_EXPORT const StringPiece GetTypeWithoutUrl(StringPiece type_url);
+
+// Returns the simple_type with the base type url (kTypeServiceBaseUrl)
+// prefixed.
+//
+// E.g:
+// GetFullTypeWithUrl("google.protobuf.Timestamp") returns the string
+// "type.googleapis.com/google.protobuf.Timestamp".
+LIBPROTOBUF_EXPORT const string GetFullTypeWithUrl(StringPiece simple_type);
+
+// Finds and returns option identified by name and option_name within the
+// provided map. Returns NULL if none found.
+LIBPROTOBUF_EXPORT const google::protobuf::Option* FindOptionOrNull(
+ const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options,
+ const string& option_name);
+
+// Finds and returns the field identified by field_name in the passed tech Type
+// object. Returns NULL if none found.
+LIBPROTOBUF_EXPORT const google::protobuf::Field* FindFieldInTypeOrNull(
+ const google::protobuf::Type* type, StringPiece field_name);
+
+// Finds and returns the EnumValue identified by enum_name in the passed tech
+// Enum object. Returns NULL if none found.
+LIBPROTOBUF_EXPORT const google::protobuf::EnumValue* FindEnumValueByNameOrNull(
+ const google::protobuf::Enum* enum_type, StringPiece enum_name);
+
+// Finds and returns the EnumValue identified by value in the passed tech
+// Enum object. Returns NULL if none found.
+LIBPROTOBUF_EXPORT const google::protobuf::EnumValue* FindEnumValueByNumberOrNull(
+ const google::protobuf::Enum* enum_type, int32 value);
+
+// Converts input to camel-case and returns it.
+// Tests are in wrappers/translator/snake2camel_objectwriter_test.cc
+// TODO(skarvaje): Isolate tests for this function and put them in
+// utility_test.cc
+LIBPROTOBUF_EXPORT string ToCamelCase(const StringPiece input);
+
+// Converts input to snake_case and returns it.
+LIBPROTOBUF_EXPORT string ToSnakeCase(StringPiece input);
+
+// Returns true if type_name represents a well-known type.
+LIBPROTOBUF_EXPORT bool IsWellKnownType(const string& type_name);
+
+// Returns true if 'bool_string' represents a valid boolean value. Only "true",
+// "false", "0" and "1" are allowed.
+LIBPROTOBUF_EXPORT bool IsValidBoolString(const string& bool_string);
+
+// Returns true if "field" is a protobuf map field based on its type.
+bool IsMap(const google::protobuf::Field& field,
+ const google::protobuf::Type& type);
+
+// Infinity/NaN-aware conversion to string.
+LIBPROTOBUF_EXPORT string DoubleAsString(double value);
+LIBPROTOBUF_EXPORT string FloatAsString(float value);
+
+// Convert from int32, int64, uint32, uint64, double or float to string.
+template <typename T>
+string ValueAsString(T value) {
+ return SimpleItoa(value);
+}
+
+template <>
+inline string ValueAsString(float value) {
+ return FloatAsString(value);
+}
+
+template <>
+inline string ValueAsString(double value) {
+ return DoubleAsString(value);
+}
+
+// Converts a string to float. Unlike safe_strtof, conversion will fail if the
+// value fits into double but not float (e.g., DBL_MAX).
+LIBPROTOBUF_EXPORT bool SafeStrToFloat(StringPiece str, float* value);
+} // namespace converter
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__
diff --git a/src/google/protobuf/util/json_format_proto3.proto b/src/google/protobuf/util/json_format_proto3.proto
new file mode 100644
index 00000000..7a282868
--- /dev/null
+++ b/src/google/protobuf/util/json_format_proto3.proto
@@ -0,0 +1,157 @@
+// 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 proto3;
+
+
+import "google/protobuf/duration.proto";
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/wrappers.proto";
+import "google/protobuf/struct.proto";
+import "google/protobuf/any.proto";
+import "google/protobuf/field_mask.proto";
+
+enum EnumType {
+ FOO = 0;
+ BAR = 1;
+}
+
+message MessageType {
+ int32 value = 1;
+}
+
+message TestMessage {
+ bool bool_value = 1;
+ int32 int32_value = 2;
+ int64 int64_value = 3;
+ uint32 uint32_value = 4;
+ uint64 uint64_value = 5;
+ float float_value = 6;
+ double double_value = 7;
+ string string_value = 8;
+ bytes bytes_value = 9;
+ EnumType enum_value = 10;
+ MessageType message_value = 11;
+
+ repeated bool repeated_bool_value = 21;
+ repeated int32 repeated_int32_value = 22;
+ repeated int64 repeated_int64_value = 23;
+ repeated uint32 repeated_uint32_value = 24;
+ repeated uint64 repeated_uint64_value = 25;
+ repeated float repeated_float_value = 26;
+ repeated double repeated_double_value = 27;
+ repeated string repeated_string_value = 28;
+ repeated bytes repeated_bytes_value = 29;
+ repeated EnumType repeated_enum_value = 30;
+ repeated MessageType repeated_message_value = 31;
+}
+
+message TestOneof {
+ // In JSON format oneof fields behave mostly the same as optional
+ // fields except that:
+ // 1. Oneof fields have field presence information and will be
+ // printed if it's set no matter whether it's the default value.
+ // 2. Multiple oneof fields in the same oneof cannot appear at the
+ // same time in the input.
+ oneof oneof_value {
+ int32 oneof_int32_value = 1;
+ string oneof_string_value = 2;
+ bytes oneof_bytes_value = 3;
+ EnumType oneof_enum_value = 4;
+ MessageType oneof_message_value = 5;
+ }
+}
+
+message TestMap {
+ map<bool, int32> bool_map = 1;
+ map<int32, int32> int32_map = 2;
+ map<int64, int32> int64_map = 3;
+ map<uint32, int32> uint32_map = 4;
+ map<uint64, int32> uint64_map = 5;
+ map<string, int32> string_map = 6;
+}
+
+message TestWrapper {
+ google.protobuf.BoolValue bool_value = 1;
+ google.protobuf.Int32Value int32_value = 2;
+ google.protobuf.Int64Value int64_value = 3;
+ google.protobuf.UInt32Value uint32_value = 4;
+ google.protobuf.UInt64Value uint64_value = 5;
+ google.protobuf.FloatValue float_value = 6;
+ google.protobuf.DoubleValue double_value = 7;
+ google.protobuf.StringValue string_value = 8;
+ google.protobuf.BytesValue bytes_value = 9;
+
+ repeated google.protobuf.BoolValue repeated_bool_value = 11;
+ repeated google.protobuf.Int32Value repeated_int32_value = 12;
+ repeated google.protobuf.Int64Value repeated_int64_value = 13;
+ repeated google.protobuf.UInt32Value repeated_uint32_value = 14;
+ repeated google.protobuf.UInt64Value repeated_uint64_value = 15;
+ repeated google.protobuf.FloatValue repeated_float_value = 16;
+ repeated google.protobuf.DoubleValue repeated_double_value = 17;
+ repeated google.protobuf.StringValue repeated_string_value = 18;
+ repeated google.protobuf.BytesValue repeated_bytes_value = 19;
+}
+
+message TestTimestamp {
+ google.protobuf.Timestamp value = 1;
+ repeated google.protobuf.Timestamp repeated_value = 2;
+}
+
+message TestDuration {
+ google.protobuf.Duration value = 1;
+ repeated google.protobuf.Duration repeated_value = 2;
+}
+
+message TestFieldMask {
+ google.protobuf.FieldMask value = 1;
+}
+
+message TestStruct {
+ google.protobuf.Struct value = 1;
+ repeated google.protobuf.Struct repeated_value = 2;
+}
+
+message TestAny {
+ google.protobuf.Any value = 1;
+ repeated google.protobuf.Any repeated_value = 2;
+}
+
+message TestValue {
+ google.protobuf.Value value = 1;
+ repeated google.protobuf.Value repeated_value = 2;
+}
+
+message TestListValue {
+ google.protobuf.ListValue value = 1;
+ repeated google.protobuf.ListValue repeated_value = 2;
+}
diff --git a/src/google/protobuf/util/json_util.cc b/src/google/protobuf/util/json_util.cc
new file mode 100644
index 00000000..6cd40fd5
--- /dev/null
+++ b/src/google/protobuf/util/json_util.cc
@@ -0,0 +1,142 @@
+// 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 <google/protobuf/util/json_util.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/util/internal/default_value_objectwriter.h>
+#include <google/protobuf/util/internal/snake2camel_objectwriter.h>
+#include <google/protobuf/util/internal/error_listener.h>
+#include <google/protobuf/util/internal/json_objectwriter.h>
+#include <google/protobuf/util/internal/json_stream_parser.h>
+#include <google/protobuf/util/internal/protostream_objectsource.h>
+#include <google/protobuf/util/internal/protostream_objectwriter.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/util/type_resolver_util.h>
+#include <google/protobuf/stubs/bytestream.h>
+#include <google/protobuf/stubs/status_macros.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+namespace internal {
+void ZeroCopyStreamByteSink::Append(const char* bytes, size_t len) {
+ while (len > 0) {
+ void* buffer;
+ int length;
+ if (!stream_->Next(&buffer, &length)) {
+ // There isn't a way for ByteSink to report errors.
+ return;
+ }
+ if (len < length) {
+ memcpy(buffer, bytes, len);
+ stream_->BackUp(length - len);
+ break;
+ } else {
+ memcpy(buffer, bytes, length);
+ bytes += length;
+ len -= length;
+ }
+ }
+}
+} // namespace internal
+
+util::Status BinaryToJsonStream(TypeResolver* resolver,
+ const string& type_url,
+ io::ZeroCopyInputStream* binary_input,
+ io::ZeroCopyOutputStream* json_output,
+ const JsonOptions& options) {
+ io::CodedInputStream in_stream(binary_input);
+ google::protobuf::Type type;
+ RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type));
+ converter::ProtoStreamObjectSource proto_source(&in_stream, resolver, type);
+ io::CodedOutputStream out_stream(json_output);
+ converter::JsonObjectWriter json_writer(options.add_whitespace ? " " : "",
+ &out_stream);
+ converter::Snake2CamelObjectWriter snake2camel_writer(&json_writer);
+ if (options.always_print_primitive_fields) {
+ converter::DefaultValueObjectWriter default_value_writer(
+ resolver, type, &snake2camel_writer);
+ return proto_source.WriteTo(&default_value_writer);
+ } else {
+ return proto_source.WriteTo(&snake2camel_writer);
+ }
+}
+
+util::Status BinaryToJsonString(TypeResolver* resolver,
+ const string& type_url,
+ const string& binary_input,
+ string* json_output,
+ const JsonOptions& options) {
+ io::ArrayInputStream input_stream(binary_input.data(), binary_input.size());
+ io::StringOutputStream output_stream(json_output);
+ return BinaryToJsonStream(resolver, type_url, &input_stream, &output_stream,
+ options);
+}
+
+util::Status JsonToBinaryStream(TypeResolver* resolver,
+ const string& type_url,
+ io::ZeroCopyInputStream* json_input,
+ io::ZeroCopyOutputStream* binary_output) {
+ google::protobuf::Type type;
+ RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type));
+ internal::ZeroCopyStreamByteSink sink(binary_output);
+ converter::NoopErrorListener listener;
+ converter::ProtoStreamObjectWriter proto_writer(resolver, type, &sink,
+ &listener);
+
+ converter::JsonStreamParser parser(&proto_writer);
+ const void* buffer;
+ int length;
+ while (json_input->Next(&buffer, &length)) {
+ if (length == 0) continue;
+ RETURN_IF_ERROR(
+ parser.Parse(StringPiece(static_cast<const char*>(buffer), length)));
+ }
+ RETURN_IF_ERROR(parser.FinishParse());
+
+ return util::Status::OK;
+}
+
+util::Status JsonToBinaryString(TypeResolver* resolver,
+ const string& type_url,
+ const string& json_input,
+ string* binary_output) {
+ io::ArrayInputStream input_stream(json_input.data(), json_input.size());
+ io::StringOutputStream output_stream(binary_output);
+ return JsonToBinaryStream(resolver, type_url, &input_stream, &output_stream);
+}
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/json_util.h b/src/google/protobuf/util/json_util.h
new file mode 100644
index 00000000..6796ea08
--- /dev/null
+++ b/src/google/protobuf/util/json_util.h
@@ -0,0 +1,136 @@
+// 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.
+
+// Utility functions to convert between protobuf binary format and proto3 JSON
+// format.
+#ifndef GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
+#define GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
+
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/bytestream.h>
+
+namespace google {
+namespace protobuf {
+namespace io {
+class ZeroCopyInputStream;
+class ZeroCopyOutputStream;
+} // namespace io
+namespace util {
+
+struct LIBPROTOBUF_EXPORT JsonOptions {
+ // Whether to add spaces, line breaks and indentation to make the JSON output
+ // easy to read.
+ bool add_whitespace;
+ // Whether to always print primitive fields. By default primitive fields with
+ // default values will be omitted in JSON joutput. For example, an int32 field
+ // set to 0 will be omitted. Set this flag to true will override the default
+ // behavior and print primitive fields regardless of their values.
+ bool always_print_primitive_fields;
+
+ JsonOptions() : add_whitespace(false),
+ always_print_primitive_fields(false) {
+ }
+};
+
+// Converts protobuf binary data to JSON.
+// The conversion will fail if:
+// 1. TypeResolver fails to resolve a type.
+// 2. input is not valid protobuf wire format, or conflicts with the type
+// information returned by TypeResolver.
+// Note that unknown fields will be discarded silently.
+LIBPROTOBUF_EXPORT util::Status BinaryToJsonStream(
+ TypeResolver* resolver,
+ const string& type_url,
+ io::ZeroCopyInputStream* binary_input,
+ io::ZeroCopyOutputStream* json_output,
+ const JsonOptions& options);
+
+inline util::Status BinaryToJsonStream(
+ TypeResolver* resolver, const string& type_url,
+ io::ZeroCopyInputStream* binary_input,
+ io::ZeroCopyOutputStream* json_output) {
+ return BinaryToJsonStream(resolver, type_url, binary_input, json_output,
+ JsonOptions());
+}
+
+LIBPROTOBUF_EXPORT util::Status BinaryToJsonString(
+ TypeResolver* resolver,
+ const string& type_url,
+ const string& binary_input,
+ string* json_output,
+ const JsonOptions& options);
+
+inline util::Status BinaryToJsonString(TypeResolver* resolver,
+ const string& type_url,
+ const string& binary_input,
+ string* json_output) {
+ return BinaryToJsonString(resolver, type_url, binary_input, json_output,
+ JsonOptions());
+}
+
+// Converts JSON data to protobuf binary format.
+// The conversion will fail if:
+// 1. TypeResolver fails to resolve a type.
+// 2. input is not valid JSON format, or conflicts with the type
+// information returned by TypeResolver.
+// 3. input has unknown fields.
+LIBPROTOBUF_EXPORT util::Status JsonToBinaryStream(
+ TypeResolver* resolver,
+ const string& type_url,
+ io::ZeroCopyInputStream* json_input,
+ io::ZeroCopyOutputStream* binary_output);
+
+LIBPROTOBUF_EXPORT util::Status JsonToBinaryString(
+ TypeResolver* resolver,
+ const string& type_url,
+ const string& json_input,
+ string* binary_output);
+
+namespace internal {
+// Internal helper class. Put in the header so we can write unit-tests for it.
+class LIBPROTOBUF_EXPORT ZeroCopyStreamByteSink : public strings::ByteSink {
+ public:
+ explicit ZeroCopyStreamByteSink(io::ZeroCopyOutputStream* stream)
+ : stream_(stream) {}
+
+ virtual void Append(const char* bytes, size_t len);
+
+ private:
+ io::ZeroCopyOutputStream* stream_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyStreamByteSink);
+};
+} // namespace internal
+
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
diff --git a/src/google/protobuf/util/json_util_test.cc b/src/google/protobuf/util/json_util_test.cc
new file mode 100644
index 00000000..8399b408
--- /dev/null
+++ b/src/google/protobuf/util/json_util_test.cc
@@ -0,0 +1,277 @@
+// 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 <google/protobuf/util/json_util.h>
+
+#include <list>
+#include <string>
+
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/util/json_format_proto3.pb.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/util/type_resolver_util.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace {
+
+using proto3::FOO;
+using proto3::BAR;
+using proto3::TestMessage;
+
+static const char kTypeUrlPrefix[] = "type.googleapis.com";
+
+static string GetTypeUrl(const Descriptor* message) {
+ return string(kTypeUrlPrefix) + "/" + message->full_name();
+}
+
+// As functions defined in json_util.h are just thin wrappers around the
+// JSON conversion code in //net/proto2/util/converter, in this test we
+// only cover some very basic cases to make sure the wrappers have forwarded
+// parameters to the underlying implementation correctly. More detailed
+// tests are contained in the //net/proto2/util/converter directory.
+class JsonUtilTest : public testing::Test {
+ protected:
+ JsonUtilTest() {
+ resolver_.reset(NewTypeResolverForDescriptorPool(
+ kTypeUrlPrefix, DescriptorPool::generated_pool()));
+ }
+
+ string ToJson(const Message& message, const JsonOptions& options) {
+ string result;
+ GOOGLE_CHECK_OK(BinaryToJsonString(resolver_.get(),
+ GetTypeUrl(message.GetDescriptor()),
+ message.SerializeAsString(), &result, options));
+ return result;
+ }
+
+ bool FromJson(const string& json, Message* message) {
+ string binary;
+ GOOGLE_CHECK_OK(JsonToBinaryString(
+ resolver_.get(), GetTypeUrl(message->GetDescriptor()), json, &binary));
+ return message->ParseFromString(binary);
+ }
+
+ google::protobuf::scoped_ptr<TypeResolver> resolver_;
+};
+
+TEST_F(JsonUtilTest, TestWhitespaces) {
+ TestMessage m;
+ m.mutable_message_value();
+
+ JsonOptions options;
+ EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options));
+ options.add_whitespace = true;
+ EXPECT_EQ(
+ "{\n"
+ " \"messageValue\": {}\n"
+ "}\n",
+ ToJson(m, options));
+}
+
+TEST_F(JsonUtilTest, TestDefaultValues) {
+ TestMessage m;
+ JsonOptions options;
+ EXPECT_EQ("{}", ToJson(m, options));
+ options.always_print_primitive_fields = true;
+ EXPECT_EQ(
+ "{\"boolValue\":false,"
+ "\"int32Value\":0,"
+ "\"int64Value\":\"0\","
+ "\"uint32Value\":0,"
+ "\"uint64Value\":\"0\","
+ "\"floatValue\":0,"
+ "\"doubleValue\":0,"
+ "\"stringValue\":\"\","
+ "\"bytesValue\":\"\","
+ // TODO(xiaofeng): The default enum value should be FOO. I believe
+ // this is a bug in DefaultValueObjectWriter.
+ "\"enumValue\":null"
+ "}",
+ ToJson(m, options));
+}
+
+TEST_F(JsonUtilTest, ParseMessage) {
+ // Some random message but good enough to verify that the parsing warpper
+ // functions are working properly.
+ string input =
+ "{\n"
+ " \"int32Value\": 1024,\n"
+ " \"repeatedInt32Value\": [1, 2],\n"
+ " \"messageValue\": {\n"
+ " \"value\": 2048\n"
+ " },\n"
+ " \"repeatedMessageValue\": [\n"
+ " {\"value\": 40}, {\"value\": 96}\n"
+ " ]\n"
+ "}\n";
+ TestMessage m;
+ ASSERT_TRUE(FromJson(input, &m));
+ EXPECT_EQ(1024, m.int32_value());
+ ASSERT_EQ(2, m.repeated_int32_value_size());
+ EXPECT_EQ(1, m.repeated_int32_value(0));
+ EXPECT_EQ(2, m.repeated_int32_value(1));
+ EXPECT_EQ(2048, m.message_value().value());
+ ASSERT_EQ(2, m.repeated_message_value_size());
+ EXPECT_EQ(40, m.repeated_message_value(0).value());
+ EXPECT_EQ(96, m.repeated_message_value(1).value());
+}
+
+typedef pair<char*, int> Segment;
+// A ZeroCopyOutputStream that writes to multiple buffers.
+class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream {
+ public:
+ explicit SegmentedZeroCopyOutputStream(list<Segment> segments)
+ : segments_(segments), last_segment_(NULL, 0), byte_count_(0) {}
+
+ virtual bool Next(void** buffer, int* length) {
+ if (segments_.empty()) {
+ return false;
+ }
+ last_segment_ = segments_.front();
+ segments_.pop_front();
+ *buffer = last_segment_.first;
+ *length = last_segment_.second;
+ byte_count_ += *length;
+ return true;
+ }
+
+ virtual void BackUp(int length) {
+ GOOGLE_CHECK(length <= last_segment_.second);
+ segments_.push_front(
+ Segment(last_segment_.first + last_segment_.second - length, length));
+ last_segment_ = Segment(last_segment_.first, last_segment_.second - length);
+ byte_count_ -= length;
+ }
+
+ virtual int64 ByteCount() const { return byte_count_; }
+
+ private:
+ list<Segment> segments_;
+ Segment last_segment_;
+ int64 byte_count_;
+};
+
+// This test splits the output buffer and also the input data into multiple
+// segments and checks that the implementation of ZeroCopyStreamByteSink
+// handles all possible cases correctly.
+TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) {
+ static const int kOutputBufferLength = 10;
+ // An exhaustive test takes too long, skip some combinations to make the test
+ // run faster.
+ static const int kSkippedPatternCount = 7;
+
+ char buffer[kOutputBufferLength];
+ for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1));
+ split_pattern += kSkippedPatternCount) {
+ // Split the buffer into small segments according to the split_pattern.
+ list<Segment> segments;
+ int segment_start = 0;
+ for (int i = 0; i < kOutputBufferLength - 1; ++i) {
+ if (split_pattern & (1 << i)) {
+ segments.push_back(
+ Segment(buffer + segment_start, i - segment_start + 1));
+ segment_start = i + 1;
+ }
+ }
+ segments.push_back(
+ Segment(buffer + segment_start, kOutputBufferLength - segment_start));
+
+ // Write exactly 10 bytes through the ByteSink.
+ string input_data = "0123456789";
+ for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
+ input_pattern += kSkippedPatternCount) {
+ memset(buffer, 0, sizeof(buffer));
+ {
+ SegmentedZeroCopyOutputStream output_stream(segments);
+ internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
+ int start = 0;
+ for (int j = 0; j < input_data.length() - 1; ++j) {
+ if (input_pattern & (1 << j)) {
+ byte_sink.Append(&input_data[start], j - start + 1);
+ start = j + 1;
+ }
+ }
+ byte_sink.Append(&input_data[start], input_data.length() - start);
+ }
+ EXPECT_EQ(input_data, string(buffer, input_data.length()));
+ }
+
+ // Write only 9 bytes through the ByteSink.
+ input_data = "012345678";
+ for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
+ input_pattern += kSkippedPatternCount) {
+ memset(buffer, 0, sizeof(buffer));
+ {
+ SegmentedZeroCopyOutputStream output_stream(segments);
+ internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
+ int start = 0;
+ for (int j = 0; j < input_data.length() - 1; ++j) {
+ if (input_pattern & (1 << j)) {
+ byte_sink.Append(&input_data[start], j - start + 1);
+ start = j + 1;
+ }
+ }
+ byte_sink.Append(&input_data[start], input_data.length() - start);
+ }
+ EXPECT_EQ(input_data, string(buffer, input_data.length()));
+ EXPECT_EQ(0, buffer[input_data.length()]);
+ }
+
+ // Write 11 bytes through the ByteSink. The extra byte will just
+ // be ignored.
+ input_data = "0123456789A";
+ for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1));
+ input_pattern += kSkippedPatternCount) {
+ memset(buffer, 0, sizeof(buffer));
+ {
+ SegmentedZeroCopyOutputStream output_stream(segments);
+ internal::ZeroCopyStreamByteSink byte_sink(&output_stream);
+ int start = 0;
+ for (int j = 0; j < input_data.length() - 1; ++j) {
+ if (input_pattern & (1 << j)) {
+ byte_sink.Append(&input_data[start], j - start + 1);
+ start = j + 1;
+ }
+ }
+ byte_sink.Append(&input_data[start], input_data.length() - start);
+ }
+ EXPECT_EQ(input_data.substr(0, kOutputBufferLength),
+ string(buffer, kOutputBufferLength));
+ }
+ }
+}
+
+} // namespace
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc
new file mode 100644
index 00000000..057b414a
--- /dev/null
+++ b/src/google/protobuf/util/message_differencer.cc
@@ -0,0 +1,1629 @@
+// 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: jschorr@google.com (Joseph Schorr)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file defines static methods and classes for comparing Protocol
+// Messages (see //google/protobuf/util/message_differencer.h for more
+// information).
+
+#include <google/protobuf/util/message_differencer.h>
+
+#include <algorithm>
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <utility>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/any.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/util/field_comparator.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+
+namespace util {
+
+// When comparing a repeated field as map, MultipleFieldMapKeyComparator can
+// be used to specify multiple fields as key for key comparison.
+// Two elements of a repeated field will be regarded as having the same key
+// iff they have the same value for every specified key field.
+// Note that you can also specify only one field as key.
+class MessageDifferencer::MultipleFieldsMapKeyComparator
+ : public MessageDifferencer::MapKeyComparator {
+ public:
+ MultipleFieldsMapKeyComparator(
+ MessageDifferencer* message_differencer,
+ const vector<vector<const FieldDescriptor*> >& key_field_paths)
+ : message_differencer_(message_differencer),
+ key_field_paths_(key_field_paths) {
+ GOOGLE_CHECK(!key_field_paths_.empty());
+ for (int i = 0; i < key_field_paths_.size(); ++i) {
+ GOOGLE_CHECK(!key_field_paths_[i].empty());
+ }
+ }
+ MultipleFieldsMapKeyComparator(
+ MessageDifferencer* message_differencer,
+ const FieldDescriptor* key)
+ : message_differencer_(message_differencer) {
+ vector<const FieldDescriptor*> key_field_path;
+ key_field_path.push_back(key);
+ key_field_paths_.push_back(key_field_path);
+ }
+ virtual bool IsMatch(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& parent_fields) const {
+ for (int i = 0; i < key_field_paths_.size(); ++i) {
+ if (!IsMatchInternal(message1, message2, parent_fields,
+ key_field_paths_[i], 0)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ private:
+ bool IsMatchInternal(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& parent_fields,
+ const vector<const FieldDescriptor*>& key_field_path,
+ int path_index) const {
+ const FieldDescriptor* field = key_field_path[path_index];
+ vector<SpecificField> current_parent_fields(parent_fields);
+ if (path_index == key_field_path.size() - 1) {
+ if (field->is_repeated()) {
+ if (!message_differencer_->CompareRepeatedField(
+ message1, message2, field, &current_parent_fields)) {
+ return false;
+ }
+ } else {
+ if (!message_differencer_->CompareFieldValueUsingParentFields(
+ message1, message2, field, -1, -1, &current_parent_fields)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ const Reflection* reflection1 = message1.GetReflection();
+ const Reflection* reflection2 = message2.GetReflection();
+ bool has_field1 = reflection1->HasField(message1, field);
+ bool has_field2 = reflection2->HasField(message2, field);
+ if (!has_field1 && !has_field2) {
+ return true;
+ }
+ if (has_field1 != has_field2) {
+ return false;
+ }
+ SpecificField specific_field;
+ specific_field.field = field;
+ current_parent_fields.push_back(specific_field);
+ return IsMatchInternal(
+ reflection1->GetMessage(message1, field),
+ reflection2->GetMessage(message2, field),
+ current_parent_fields,
+ key_field_path,
+ path_index + 1);
+ }
+ }
+ MessageDifferencer* message_differencer_;
+ vector<vector<const FieldDescriptor*> > key_field_paths_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultipleFieldsMapKeyComparator);
+};
+
+bool MessageDifferencer::Equals(const Message& message1,
+ const Message& message2) {
+ MessageDifferencer differencer;
+
+ return differencer.Compare(message1, message2);
+}
+
+bool MessageDifferencer::Equivalent(const Message& message1,
+ const Message& message2) {
+ MessageDifferencer differencer;
+ differencer.set_message_field_comparison(MessageDifferencer::EQUIVALENT);
+
+ return differencer.Compare(message1, message2);
+}
+
+bool MessageDifferencer::ApproximatelyEquals(const Message& message1,
+ const Message& message2) {
+ MessageDifferencer differencer;
+ differencer.set_float_comparison(
+ MessageDifferencer::APPROXIMATE);
+
+ return differencer.Compare(message1, message2);
+}
+
+bool MessageDifferencer::ApproximatelyEquivalent(const Message& message1,
+ const Message& message2) {
+ MessageDifferencer differencer;
+ differencer.set_message_field_comparison(MessageDifferencer::EQUIVALENT);
+ differencer.set_float_comparison(MessageDifferencer::APPROXIMATE);
+
+ return differencer.Compare(message1, message2);
+}
+
+// ===========================================================================
+
+MessageDifferencer::MessageDifferencer()
+ : reporter_(NULL),
+ field_comparator_(NULL),
+ message_field_comparison_(EQUAL),
+ scope_(FULL),
+ repeated_field_comparison_(AS_LIST),
+ report_matches_(false),
+ output_string_(NULL) { }
+
+MessageDifferencer::~MessageDifferencer() {
+ for (int i = 0; i < owned_key_comparators_.size(); ++i) {
+ delete owned_key_comparators_[i];
+ }
+ for (int i = 0; i < ignore_criteria_.size(); ++i) {
+ delete ignore_criteria_[i];
+ }
+}
+
+void MessageDifferencer::set_field_comparator(FieldComparator* comparator) {
+ GOOGLE_CHECK(comparator) << "Field comparator can't be NULL.";
+ field_comparator_ = comparator;
+}
+
+void MessageDifferencer::set_message_field_comparison(
+ MessageFieldComparison comparison) {
+ message_field_comparison_ = comparison;
+}
+
+void MessageDifferencer::set_scope(Scope scope) {
+ scope_ = scope;
+}
+
+MessageDifferencer::Scope MessageDifferencer::scope() {
+ return scope_;
+}
+
+void MessageDifferencer::set_float_comparison(FloatComparison comparison) {
+ default_field_comparator_.set_float_comparison(
+ comparison == EXACT ?
+ DefaultFieldComparator::EXACT : DefaultFieldComparator::APPROXIMATE);
+}
+
+void MessageDifferencer::set_repeated_field_comparison(
+ RepeatedFieldComparison comparison) {
+ repeated_field_comparison_ = comparison;
+}
+
+void MessageDifferencer::TreatAsSet(const FieldDescriptor* field) {
+ GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
+ << field->full_name();
+ const MapKeyComparator* key_comparator = GetMapKeyComparator(field);
+ GOOGLE_CHECK(key_comparator == NULL)
+ << "Cannot treat this repeated field as both Map and Set for"
+ << " comparison. Field name is: " << field->full_name();
+ set_fields_.insert(field);
+}
+
+void MessageDifferencer::TreatAsMap(const FieldDescriptor* field,
+ const FieldDescriptor* key) {
+ GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
+ << field->full_name();
+ GOOGLE_CHECK_EQ(FieldDescriptor::CPPTYPE_MESSAGE, field->cpp_type())
+ << "Field has to be message type. Field name is: "
+ << field->full_name();
+ GOOGLE_CHECK(key->containing_type() == field->message_type())
+ << key->full_name()
+ << " must be a direct subfield within the repeated field "
+ << field->full_name() << ", not " << key->containing_type()->full_name();
+ GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end())
+ << "Cannot treat this repeated field as both Map and Set for "
+ << "comparison.";
+ MapKeyComparator* key_comparator =
+ new MultipleFieldsMapKeyComparator(this, key);
+ owned_key_comparators_.push_back(key_comparator);
+ map_field_key_comparator_[field] = key_comparator;
+}
+
+void MessageDifferencer::TreatAsMapWithMultipleFieldsAsKey(
+ const FieldDescriptor* field,
+ const vector<const FieldDescriptor*>& key_fields) {
+ vector<vector<const FieldDescriptor*> > key_field_paths;
+ for (int i = 0; i < key_fields.size(); ++i) {
+ vector<const FieldDescriptor*> key_field_path;
+ key_field_path.push_back(key_fields[i]);
+ key_field_paths.push_back(key_field_path);
+ }
+ TreatAsMapWithMultipleFieldPathsAsKey(field, key_field_paths);
+}
+
+void MessageDifferencer::TreatAsMapWithMultipleFieldPathsAsKey(
+ const FieldDescriptor* field,
+ const vector<vector<const FieldDescriptor*> >& key_field_paths) {
+ GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
+ << field->full_name();
+ GOOGLE_CHECK_EQ(FieldDescriptor::CPPTYPE_MESSAGE, field->cpp_type())
+ << "Field has to be message type. Field name is: "
+ << field->full_name();
+ for (int i = 0; i < key_field_paths.size(); ++i) {
+ const vector<const FieldDescriptor*>& key_field_path = key_field_paths[i];
+ for (int j = 0; j < key_field_path.size(); ++j) {
+ const FieldDescriptor* parent_field =
+ j == 0 ? field : key_field_path[j - 1];
+ const FieldDescriptor* child_field = key_field_path[j];
+ GOOGLE_CHECK(child_field->containing_type() == parent_field->message_type())
+ << child_field->full_name()
+ << " must be a direct subfield within the field: "
+ << parent_field->full_name();
+ if (j != 0) {
+ GOOGLE_CHECK_EQ(FieldDescriptor::CPPTYPE_MESSAGE, parent_field->cpp_type())
+ << parent_field->full_name() << " has to be of type message.";
+ GOOGLE_CHECK(!parent_field->is_repeated())
+ << parent_field->full_name() << " cannot be a repeated field.";
+ }
+ }
+ }
+ GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end())
+ << "Cannot treat this repeated field as both Map and Set for "
+ << "comparison.";
+ MapKeyComparator* key_comparator =
+ new MultipleFieldsMapKeyComparator(this, key_field_paths);
+ owned_key_comparators_.push_back(key_comparator);
+ map_field_key_comparator_[field] = key_comparator;
+}
+
+void MessageDifferencer::TreatAsMapUsingKeyComparator(
+ const FieldDescriptor* field,
+ const MapKeyComparator* key_comparator) {
+ GOOGLE_CHECK(field->is_repeated()) << "Field must be repeated: "
+ << field->full_name();
+ GOOGLE_CHECK_EQ(FieldDescriptor::CPPTYPE_MESSAGE, field->cpp_type())
+ << "Field has to be message type. Field name is: "
+ << field->full_name();
+ GOOGLE_CHECK(set_fields_.find(field) == set_fields_.end())
+ << "Cannot treat this repeated field as both Map and Set for "
+ << "comparison.";
+ map_field_key_comparator_[field] = key_comparator;
+}
+
+void MessageDifferencer::AddIgnoreCriteria(IgnoreCriteria* ignore_criteria) {
+ ignore_criteria_.push_back(ignore_criteria);
+}
+
+void MessageDifferencer::IgnoreField(const FieldDescriptor* field) {
+ ignored_fields_.insert(field);
+}
+
+void MessageDifferencer::SetFractionAndMargin(const FieldDescriptor* field,
+ double fraction, double margin) {
+ default_field_comparator_.SetFractionAndMargin(field, fraction, margin);
+}
+
+void MessageDifferencer::ReportDifferencesToString(string* output) {
+ GOOGLE_DCHECK(output) << "Specified output string was NULL";
+
+ output_string_ = output;
+ output_string_->clear();
+}
+
+void MessageDifferencer::ReportDifferencesTo(Reporter* reporter) {
+ // If an output string is set, clear it to prevent
+ // it superceding the specified reporter.
+ if (output_string_) {
+ output_string_ = NULL;
+ }
+
+ reporter_ = reporter;
+}
+
+bool MessageDifferencer::FieldBefore(const FieldDescriptor* field1,
+ const FieldDescriptor* field2) {
+ // Handle sentinel values (i.e. make sure NULLs are always ordered
+ // at the end of the list).
+ if (field1 == NULL) {
+ return false;
+ }
+
+ if (field2 == NULL) {
+ return true;
+ }
+
+ // Always order fields by their tag number
+ return (field1->number() < field2->number());
+}
+
+bool MessageDifferencer::Compare(const Message& message1,
+ const Message& message2) {
+ vector<SpecificField> parent_fields;
+
+ bool result = false;
+
+ // Setup the internal reporter if need be.
+ if (output_string_) {
+ io::StringOutputStream output_stream(output_string_);
+ StreamReporter reporter(&output_stream);
+ reporter_ = &reporter;
+ result = Compare(message1, message2, &parent_fields);
+ reporter_ = NULL;
+ } else {
+ result = Compare(message1, message2, &parent_fields);
+ }
+
+ return result;
+}
+
+bool MessageDifferencer::CompareWithFields(
+ const Message& message1,
+ const Message& message2,
+ const vector<const FieldDescriptor*>& message1_fields_arg,
+ const vector<const FieldDescriptor*>& message2_fields_arg) {
+ if (message1.GetDescriptor() != message2.GetDescriptor()) {
+ GOOGLE_LOG(DFATAL) << "Comparison between two messages with different "
+ << "descriptors.";
+ return false;
+ }
+
+ vector<SpecificField> parent_fields;
+
+ bool result = false;
+
+ vector<const FieldDescriptor*> message1_fields(message1_fields_arg);
+ vector<const FieldDescriptor*> message2_fields(message2_fields_arg);
+
+ std::sort(message1_fields.begin(), message1_fields.end(), FieldBefore);
+ std::sort(message2_fields.begin(), message2_fields.end(), FieldBefore);
+ // Append NULL sentinel values.
+ message1_fields.push_back(NULL);
+ message2_fields.push_back(NULL);
+
+ // Setup the internal reporter if need be.
+ if (output_string_) {
+ io::StringOutputStream output_stream(output_string_);
+ StreamReporter reporter(&output_stream);
+ reporter_ = &reporter;
+ result = CompareRequestedFieldsUsingSettings(
+ message1, message2, message1_fields, message2_fields, &parent_fields);
+ reporter_ = NULL;
+ } else {
+ result = CompareRequestedFieldsUsingSettings(
+ message1, message2, message1_fields, message2_fields, &parent_fields);
+ }
+
+ return result;
+}
+
+bool MessageDifferencer::Compare(
+ const Message& message1,
+ const Message& message2,
+ vector<SpecificField>* parent_fields) {
+ const Descriptor* descriptor1 = message1.GetDescriptor();
+ const Descriptor* descriptor2 = message2.GetDescriptor();
+ if (descriptor1 != descriptor2) {
+ GOOGLE_LOG(DFATAL) << "Comparison between two messages with different "
+ << "descriptors.";
+ return false;
+ }
+ // Expand google.protobuf.Any payload if possible.
+ if (descriptor1->full_name() == internal::kAnyFullTypeName) {
+ google::protobuf::scoped_ptr<Message> data1;
+ google::protobuf::scoped_ptr<Message> data2;
+ if (UnpackAny(message1, &data1) && UnpackAny(message2, &data2)) {
+ return Compare(*data1, *data2, parent_fields);
+ }
+ }
+ const Reflection* reflection1 = message1.GetReflection();
+ const Reflection* reflection2 = message2.GetReflection();
+
+ // Retrieve all the set fields, including extensions.
+ vector<const FieldDescriptor*> message1_fields;
+ vector<const FieldDescriptor*> message2_fields;
+
+ reflection1->ListFields(message1, &message1_fields);
+ reflection2->ListFields(message2, &message2_fields);
+
+ // Add sentinel values to deal with the
+ // case where the number of the fields in
+ // each list are different.
+ message1_fields.push_back(NULL);
+ message2_fields.push_back(NULL);
+
+ bool unknown_compare_result = true;
+ // Ignore unknown fields in EQUIVALENT mode
+ if (message_field_comparison_ != EQUIVALENT) {
+ const google::protobuf::UnknownFieldSet* unknown_field_set1 =
+ &reflection1->GetUnknownFields(message1);
+ const google::protobuf::UnknownFieldSet* unknown_field_set2 =
+ &reflection2->GetUnknownFields(message2);
+ if (!CompareUnknownFields(message1, message2,
+ *unknown_field_set1, *unknown_field_set2,
+ parent_fields)) {
+ if (reporter_ == NULL) {
+ return false;
+ };
+ unknown_compare_result = false;
+ }
+ }
+
+ return CompareRequestedFieldsUsingSettings(
+ message1, message2,
+ message1_fields, message2_fields,
+ parent_fields) && unknown_compare_result;
+}
+
+bool MessageDifferencer::CompareRequestedFieldsUsingSettings(
+ const Message& message1,
+ const Message& message2,
+ const vector<const FieldDescriptor*>& message1_fields,
+ const vector<const FieldDescriptor*>& message2_fields,
+ vector<SpecificField>* parent_fields) {
+ if (scope_ == FULL) {
+ if (message_field_comparison_ == EQUIVALENT) {
+ // We need to merge the field lists of both messages (i.e.
+ // we are merely checking for a difference in field values,
+ // rather than the addition or deletion of fields).
+ vector<const FieldDescriptor*> fields_union;
+ CombineFields(message1_fields, FULL, message2_fields, FULL,
+ &fields_union);
+ return CompareWithFieldsInternal(message1, message2, fields_union,
+ fields_union, parent_fields);
+ } else {
+ // Simple equality comparison, use the unaltered field lists.
+ return CompareWithFieldsInternal(message1, message2, message1_fields,
+ message2_fields, parent_fields);
+ }
+ } else {
+ if (message_field_comparison_ == EQUIVALENT) {
+ // We use the list of fields for message1 for both messages when
+ // comparing. This way, extra fields in message2 are ignored,
+ // and missing fields in message2 use their default value.
+ return CompareWithFieldsInternal(message1, message2, message1_fields,
+ message1_fields, parent_fields);
+ } else {
+ // We need to consider the full list of fields for message1
+ // but only the intersection for message2. This way, any fields
+ // only present in message2 will be ignored, but any fields only
+ // present in message1 will be marked as a difference.
+ vector<const FieldDescriptor*> fields_intersection;
+ CombineFields(message1_fields, PARTIAL, message2_fields, PARTIAL,
+ &fields_intersection);
+ return CompareWithFieldsInternal(message1, message2, message1_fields,
+ fields_intersection, parent_fields);
+ }
+ }
+}
+
+void MessageDifferencer::CombineFields(
+ const vector<const FieldDescriptor*>& fields1,
+ Scope fields1_scope,
+ const vector<const FieldDescriptor*>& fields2,
+ Scope fields2_scope,
+ vector<const FieldDescriptor*>* combined_fields) {
+
+ int index1 = 0;
+ int index2 = 0;
+
+ while (index1 < fields1.size() && index2 < fields2.size()) {
+ const FieldDescriptor* field1 = fields1[index1];
+ const FieldDescriptor* field2 = fields2[index2];
+
+ if (FieldBefore(field1, field2)) {
+ if (fields1_scope == FULL) {
+ combined_fields->push_back(fields1[index1]);
+ }
+ ++index1;
+ } else if (FieldBefore(field2, field1)) {
+ if (fields2_scope == FULL) {
+ combined_fields->push_back(fields2[index2]);
+ }
+ ++index2;
+ } else {
+ combined_fields->push_back(fields1[index1]);
+ ++index1;
+ ++index2;
+ }
+ }
+}
+
+bool MessageDifferencer::CompareWithFieldsInternal(
+ const Message& message1,
+ const Message& message2,
+ const vector<const FieldDescriptor*>& message1_fields,
+ const vector<const FieldDescriptor*>& message2_fields,
+ vector<SpecificField>* parent_fields) {
+ bool isDifferent = false;
+ int field_index1 = 0;
+ int field_index2 = 0;
+
+ const Reflection* reflection1 = message1.GetReflection();
+ const Reflection* reflection2 = message2.GetReflection();
+
+ while (true) {
+ const FieldDescriptor* field1 = message1_fields[field_index1];
+ const FieldDescriptor* field2 = message2_fields[field_index2];
+
+ // Once we have reached sentinel values, we are done the comparison.
+ if (field1 == NULL && field2 == NULL) {
+ break;
+ }
+
+ // Check for differences in the field itself.
+ if (FieldBefore(field1, field2)) {
+ // Field 1 is not in the field list for message 2.
+ if (IsIgnored(message1, message2, field1, *parent_fields)) {
+ // We are ignoring field1. Report the ignore and move on to
+ // the next field in message1_fields.
+ if (reporter_ != NULL) {
+ SpecificField specific_field;
+ specific_field.field = field1;
+
+ parent_fields->push_back(specific_field);
+ reporter_->ReportIgnored(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ }
+ ++field_index1;
+ continue;
+ }
+
+ if (reporter_ != NULL) {
+ int count = field1->is_repeated() ?
+ reflection1->FieldSize(message1, field1) : 1;
+
+ for (int i = 0; i < count; ++i) {
+ SpecificField specific_field;
+ specific_field.field = field1;
+ specific_field.index = field1->is_repeated() ? i : -1;
+
+ parent_fields->push_back(specific_field);
+ reporter_->ReportDeleted(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ }
+
+ isDifferent = true;
+ } else {
+ return false;
+ }
+
+ ++field_index1;
+ continue;
+ } else if (FieldBefore(field2, field1)) {
+ // Field 2 is not in the field list for message 1.
+ if (IsIgnored(message1, message2, field2, *parent_fields)) {
+ // We are ignoring field2. Report the ignore and move on to
+ // the next field in message2_fields.
+ if (reporter_ != NULL) {
+ SpecificField specific_field;
+ specific_field.field = field2;
+
+ parent_fields->push_back(specific_field);
+ reporter_->ReportIgnored(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ }
+ ++field_index2;
+ continue;
+ }
+
+ if (reporter_ != NULL) {
+ int count = field2->is_repeated() ?
+ reflection2->FieldSize(message2, field2) : 1;
+
+ for (int i = 0; i < count; ++i) {
+ SpecificField specific_field;
+ specific_field.field = field2;
+ specific_field.index = field2->is_repeated() ? i : -1;
+ specific_field.new_index = specific_field.index;
+
+ parent_fields->push_back(specific_field);
+ reporter_->ReportAdded(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ }
+
+ isDifferent = true;
+ } else {
+ return false;
+ }
+
+ ++field_index2;
+ continue;
+ }
+
+ // By this point, field1 and field2 are guarenteed to point to the same
+ // field, so we can now compare the values.
+ if (IsIgnored(message1, message2, field1, *parent_fields)) {
+ // Ignore this field. Report and move on.
+ if (reporter_ != NULL) {
+ SpecificField specific_field;
+ specific_field.field = field1;
+
+ parent_fields->push_back(specific_field);
+ reporter_->ReportIgnored(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ }
+
+ ++field_index1;
+ ++field_index2;
+ continue;
+ }
+
+ bool fieldDifferent = false;
+ if (field1->is_repeated()) {
+ fieldDifferent = !CompareRepeatedField(message1, message2, field1,
+ parent_fields);
+ if (fieldDifferent) {
+ if (reporter_ == NULL) return false;
+ isDifferent = true;
+ }
+ } else {
+ fieldDifferent = !CompareFieldValueUsingParentFields(
+ message1, message2, field1, -1, -1, parent_fields);
+
+ // If we have found differences, either report them or terminate if
+ // no reporter is present.
+ if (fieldDifferent && reporter_ == NULL) {
+ return false;
+ }
+
+ if (reporter_ != NULL) {
+ SpecificField specific_field;
+ specific_field.field = field1;
+ parent_fields->push_back(specific_field);
+ if (fieldDifferent) {
+ reporter_->ReportModified(message1, message2, *parent_fields);
+ isDifferent = true;
+ } else if (report_matches_) {
+ reporter_->ReportMatched(message1, message2, *parent_fields);
+ }
+ parent_fields->pop_back();
+ }
+ }
+ // Increment the field indicies.
+ ++field_index1;
+ ++field_index2;
+ }
+
+ return !isDifferent;
+}
+
+bool MessageDifferencer::IsMatch(const FieldDescriptor* repeated_field,
+ const MapKeyComparator* key_comparator,
+ const Message* message1,
+ const Message* message2,
+ const vector<SpecificField>& parent_fields,
+ int index1, int index2) {
+ vector<SpecificField> current_parent_fields(parent_fields);
+ if (repeated_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+ return CompareFieldValueUsingParentFields(
+ *message1, *message2, repeated_field, index1, index2,
+ &current_parent_fields);
+ }
+ // Back up the Reporter and output_string_. They will be reset in the
+ // following code.
+ Reporter* backup_reporter = reporter_;
+ string* output_string = output_string_;
+ reporter_ = NULL;
+ output_string_ = NULL;
+ bool match;
+
+ if (key_comparator == NULL) {
+ match = CompareFieldValueUsingParentFields(
+ *message1, *message2, repeated_field, index1, index2,
+ &current_parent_fields);
+ } else {
+ const Reflection* reflection1 = message1->GetReflection();
+ const Reflection* reflection2 = message2->GetReflection();
+ const Message& m1 =
+ reflection1->GetRepeatedMessage(*message1, repeated_field, index1);
+ const Message& m2 =
+ reflection2->GetRepeatedMessage(*message2, repeated_field, index2);
+ SpecificField specific_field;
+ specific_field.field = repeated_field;
+ current_parent_fields.push_back(specific_field);
+ match = key_comparator->IsMatch(m1, m2, current_parent_fields);
+ }
+
+ reporter_ = backup_reporter;
+ output_string_ = output_string;
+ return match;
+}
+
+bool MessageDifferencer::CompareRepeatedField(
+ const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* repeated_field,
+ vector<SpecificField>* parent_fields) {
+ // the input FieldDescriptor is guaranteed to be repeated field.
+ const Reflection* reflection1 = message1.GetReflection();
+ const Reflection* reflection2 = message2.GetReflection();
+ const int count1 = reflection1->FieldSize(message1, repeated_field);
+ const int count2 = reflection2->FieldSize(message2, repeated_field);
+ const bool treated_as_subset = IsTreatedAsSubset(repeated_field);
+
+ // If the field is not treated as subset and no detailed reports is needed,
+ // we do a quick check on the number of the elements to avoid unnecessary
+ // comparison.
+ if (count1 != count2 && reporter_ == NULL && !treated_as_subset) {
+ return false;
+ }
+ // A match can never be found if message1 has more items than message2.
+ if (count1 > count2 && reporter_ == NULL) {
+ return false;
+ }
+
+ // These two list are used for store the index of the correspondent
+ // element in peer repeated field.
+ vector<int> match_list1;
+ vector<int> match_list2;
+
+ // Try to match indices of the repeated fields. Return false if match fails
+ // and there's no detailed report needed.
+ if (!MatchRepeatedFieldIndices(message1, message2, repeated_field,
+ *parent_fields, &match_list1, &match_list2) &&
+ reporter_ == NULL) {
+ return false;
+ }
+
+ bool fieldDifferent = false;
+ SpecificField specific_field;
+ specific_field.field = repeated_field;
+
+ // At this point, we have already matched pairs of fields (with the reporting
+ // to be done later). Now to check if the paired elements are different.
+ for (int i = 0; i < count1; i++) {
+ if (match_list1[i] == -1) continue;
+ specific_field.index = i;
+ specific_field.new_index = match_list1[i];
+
+ const bool result = CompareFieldValueUsingParentFields(
+ message1, message2, repeated_field, i, specific_field.new_index,
+ parent_fields);
+
+ // If we have found differences, either report them or terminate if
+ // no reporter is present. Note that ReportModified, ReportMoved, and
+ // ReportMatched are all mutually exclusive.
+ if (!result) {
+ if (reporter_ == NULL) return false;
+ parent_fields->push_back(specific_field);
+ reporter_->ReportModified(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ fieldDifferent = true;
+ } else if (reporter_ != NULL &&
+ specific_field.index != specific_field.new_index) {
+ parent_fields->push_back(specific_field);
+ reporter_->ReportMoved(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ } else if (report_matches_ && reporter_ != NULL) {
+ parent_fields->push_back(specific_field);
+ reporter_->ReportMatched(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ }
+ }
+
+ // Report any remaining additions or deletions.
+ for (int i = 0; i < count2; ++i) {
+ if (match_list2[i] != -1) continue;
+ if (!treated_as_subset) {
+ fieldDifferent = true;
+ }
+
+ if (reporter_ == NULL) continue;
+ specific_field.index = i;
+ specific_field.new_index = i;
+ parent_fields->push_back(specific_field);
+ reporter_->ReportAdded(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ }
+
+ for (int i = 0; i < count1; ++i) {
+ if (match_list1[i] != -1) continue;
+ specific_field.index = i;
+ parent_fields->push_back(specific_field);
+ reporter_->ReportDeleted(message1, message2, *parent_fields);
+ parent_fields->pop_back();
+ fieldDifferent = true;
+ }
+ return !fieldDifferent;
+}
+
+bool MessageDifferencer::CompareFieldValue(const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* field,
+ int index1,
+ int index2) {
+ return CompareFieldValueUsingParentFields(message1, message2, field, index1,
+ index2, NULL);
+}
+
+bool MessageDifferencer::CompareFieldValueUsingParentFields(
+ const Message& message1, const Message& message2,
+ const FieldDescriptor* field, int index1, int index2,
+ vector<SpecificField>* parent_fields) {
+ FieldContext field_context(parent_fields);
+ FieldComparator::ComparisonResult result = GetFieldComparisonResult(
+ message1, message2, field, index1, index2, &field_context);
+
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
+ result == FieldComparator::RECURSE) {
+ // Get the nested messages and compare them using one of the Compare
+ // methods.
+ const Reflection* reflection1 = message1.GetReflection();
+ const Reflection* reflection2 = message2.GetReflection();
+ const Message& m1 = field->is_repeated() ?
+ reflection1->GetRepeatedMessage(message1, field, index1) :
+ reflection1->GetMessage(message1, field);
+ const Message& m2 = field->is_repeated() ?
+ reflection2->GetRepeatedMessage(message2, field, index2) :
+ reflection2->GetMessage(message2, field);
+
+ // parent_fields is used in calls to Reporter methods.
+ if (parent_fields != NULL) {
+ // Append currently compared field to the end of parent_fields.
+ SpecificField specific_field;
+ specific_field.field = field;
+ specific_field.index = index1;
+ specific_field.new_index = index2;
+ parent_fields->push_back(specific_field);
+ const bool compare_result = Compare(m1, m2, parent_fields);
+ parent_fields->pop_back();
+ return compare_result;
+ } else {
+ // Recreates parent_fields as if m1 and m2 had no parents.
+ return Compare(m1, m2);
+ }
+ } else {
+ return (result == FieldComparator::SAME);
+ }
+}
+
+bool MessageDifferencer::CheckPathChanged(
+ const vector<SpecificField>& field_path) {
+ for (int i = 0; i < field_path.size(); ++i) {
+ if (field_path[i].index != field_path[i].new_index) return true;
+ }
+ return false;
+}
+
+bool MessageDifferencer::IsTreatedAsSet(const FieldDescriptor* field) {
+ if (!field->is_repeated()) return false;
+ if (field->is_map()) return true;
+ if (repeated_field_comparison_ == AS_SET) return true;
+ return (set_fields_.find(field) != set_fields_.end());
+}
+
+bool MessageDifferencer::IsTreatedAsSubset(const FieldDescriptor* field) {
+ return scope_ == PARTIAL &&
+ (IsTreatedAsSet(field) || GetMapKeyComparator(field) != NULL);
+}
+
+bool MessageDifferencer::IsIgnored(
+ const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* field,
+ const vector<SpecificField>& parent_fields) {
+ if (ignored_fields_.find(field) != ignored_fields_.end()) {
+ return true;
+ }
+ for (int i = 0; i < ignore_criteria_.size(); ++i) {
+ if (ignore_criteria_[i]->IsIgnored(message1, message2, field,
+ parent_fields)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+const MessageDifferencer::MapKeyComparator* MessageDifferencer
+ ::GetMapKeyComparator(const FieldDescriptor* field) {
+ if (!field->is_repeated()) return NULL;
+ if (map_field_key_comparator_.find(field) !=
+ map_field_key_comparator_.end()) {
+ return map_field_key_comparator_[field];
+ }
+ return NULL;
+}
+
+namespace {
+
+typedef pair<int, const UnknownField*> IndexUnknownFieldPair;
+
+struct UnknownFieldOrdering {
+ inline bool operator()(const IndexUnknownFieldPair& a,
+ const IndexUnknownFieldPair& b) const {
+ if (a.second->number() < b.second->number()) return true;
+ if (a.second->number() > b.second->number()) return false;
+ return a.second->type() < b.second->type();
+ }
+};
+
+} // namespace
+
+bool MessageDifferencer::UnpackAny(const Message& any,
+ google::protobuf::scoped_ptr<Message>* data) {
+ const Reflection* reflection = any.GetReflection();
+ const FieldDescriptor* type_url_field;
+ const FieldDescriptor* value_field;
+ if (!internal::GetAnyFieldDescriptors(any, &type_url_field, &value_field)) {
+ return false;
+ }
+ const string& type_url = reflection->GetString(any, type_url_field);
+ string full_type_name;
+ if (!internal::ParseAnyTypeUrl(type_url, &full_type_name)) {
+ return false;
+ }
+
+ const google::protobuf::Descriptor* desc =
+ any.GetDescriptor()->file()->pool()->FindMessageTypeByName(
+ full_type_name);
+ if (desc == NULL) {
+ GOOGLE_LOG(ERROR) << "Proto type '" << full_type_name << "' not found";
+ return false;
+ }
+
+ if (dynamic_message_factory_ == NULL) {
+ dynamic_message_factory_.reset(new DynamicMessageFactory());
+ }
+ data->reset(dynamic_message_factory_->GetPrototype(desc)->New());
+ string serialized_value = reflection->GetString(any, value_field);
+ if (!(*data)->ParseFromString(serialized_value)) {
+ GOOGLE_LOG(ERROR) << "Failed to parse value for " << full_type_name;
+ return false;
+ }
+ return true;
+}
+
+bool MessageDifferencer::CompareUnknownFields(
+ const Message& message1, const Message& message2,
+ const google::protobuf::UnknownFieldSet& unknown_field_set1,
+ const google::protobuf::UnknownFieldSet& unknown_field_set2,
+ vector<SpecificField>* parent_field) {
+ // Ignore unknown fields in EQUIVALENT mode.
+ if (message_field_comparison_ == EQUIVALENT) return true;
+
+ if (unknown_field_set1.empty() && unknown_field_set2.empty()) {
+ return true;
+ }
+
+ bool is_different = false;
+
+ // We first sort the unknown fields by field number and type (in other words,
+ // in tag order), making sure to preserve ordering of values with the same
+ // tag. This allows us to report only meaningful differences between the
+ // two sets -- that is, differing values for the same tag. We use
+ // IndexUnknownFieldPairs to keep track of the field's original index for
+ // reporting purposes.
+ vector<IndexUnknownFieldPair> fields1; // unknown_field_set1, sorted
+ vector<IndexUnknownFieldPair> fields2; // unknown_field_set2, sorted
+ fields1.reserve(unknown_field_set1.field_count());
+ fields2.reserve(unknown_field_set2.field_count());
+
+ for (int i = 0; i < unknown_field_set1.field_count(); i++) {
+ fields1.push_back(std::make_pair(i, &unknown_field_set1.field(i)));
+ }
+ for (int i = 0; i < unknown_field_set2.field_count(); i++) {
+ fields2.push_back(std::make_pair(i, &unknown_field_set2.field(i)));
+ }
+
+ UnknownFieldOrdering is_before;
+ std::stable_sort(fields1.begin(), fields1.end(), is_before);
+ std::stable_sort(fields2.begin(), fields2.end(), is_before);
+
+ // In order to fill in SpecificField::index, we have to keep track of how
+ // many values we've seen with the same field number and type.
+ // current_repeated points at the first field in this range, and
+ // current_repeated_start{1,2} are the indexes of the first field in the
+ // range within fields1 and fields2.
+ const UnknownField* current_repeated = NULL;
+ int current_repeated_start1 = 0;
+ int current_repeated_start2 = 0;
+
+ // Now that we have two sorted lists, we can detect fields which appear only
+ // in one list or the other by traversing them simultaneously.
+ int index1 = 0;
+ int index2 = 0;
+ while (index1 < fields1.size() || index2 < fields2.size()) {
+ enum { ADDITION, DELETION, MODIFICATION, COMPARE_GROUPS,
+ NO_CHANGE } change_type;
+
+ // focus_field is the field we're currently reporting on. (In the case
+ // of a modification, it's the field on the left side.)
+ const UnknownField* focus_field;
+ bool match = false;
+
+ if (index2 == fields2.size() ||
+ (index1 < fields1.size() &&
+ is_before(fields1[index1], fields2[index2]))) {
+ // fields1[index1] is not present in fields2.
+ change_type = DELETION;
+ focus_field = fields1[index1].second;
+ } else if (index1 == fields1.size() ||
+ is_before(fields2[index2], fields1[index1])) {
+ // fields2[index2] is not present in fields1.
+ if (scope_ == PARTIAL) {
+ // Ignore.
+ ++index2;
+ continue;
+ }
+ change_type = ADDITION;
+ focus_field = fields2[index2].second;
+ } else {
+ // Field type and number are the same. See if the values differ.
+ change_type = MODIFICATION;
+ focus_field = fields1[index1].second;
+
+ switch (focus_field->type()) {
+ case UnknownField::TYPE_VARINT:
+ match = fields1[index1].second->varint() ==
+ fields2[index2].second->varint();
+ break;
+ case UnknownField::TYPE_FIXED32:
+ match = fields1[index1].second->fixed32() ==
+ fields2[index2].second->fixed32();
+ break;
+ case UnknownField::TYPE_FIXED64:
+ match = fields1[index1].second->fixed64() ==
+ fields2[index2].second->fixed64();
+ break;
+ case UnknownField::TYPE_LENGTH_DELIMITED:
+ match = fields1[index1].second->length_delimited() ==
+ fields2[index2].second->length_delimited();
+ break;
+ case UnknownField::TYPE_GROUP:
+ // We must deal with this later, after building the SpecificField.
+ change_type = COMPARE_GROUPS;
+ break;
+ }
+ if (match && change_type != COMPARE_GROUPS) {
+ change_type = NO_CHANGE;
+ }
+ }
+
+ if (current_repeated == NULL ||
+ focus_field->number() != current_repeated->number() ||
+ focus_field->type() != current_repeated->type()) {
+ // We've started a new repeated field.
+ current_repeated = focus_field;
+ current_repeated_start1 = index1;
+ current_repeated_start2 = index2;
+ }
+
+ if (change_type == NO_CHANGE && reporter_ == NULL) {
+ // Fields were already compared and matched and we have no reporter.
+ ++index1;
+ ++index2;
+ continue;
+ }
+
+ if (change_type == ADDITION || change_type == DELETION ||
+ change_type == MODIFICATION) {
+ if (reporter_ == NULL) {
+ // We found a difference and we have no reproter.
+ return false;
+ }
+ is_different = true;
+ }
+
+ // Build the SpecificField. This is slightly complicated.
+ SpecificField specific_field;
+ specific_field.unknown_field_number = focus_field->number();
+ specific_field.unknown_field_type = focus_field->type();
+
+ specific_field.unknown_field_set1 = &unknown_field_set1;
+ specific_field.unknown_field_set2 = &unknown_field_set2;
+
+ if (change_type != ADDITION) {
+ specific_field.unknown_field_index1 = fields1[index1].first;
+ }
+ if (change_type != DELETION) {
+ specific_field.unknown_field_index2 = fields2[index2].first;
+ }
+
+ // Calculate the field index.
+ if (change_type == ADDITION) {
+ specific_field.index = index2 - current_repeated_start2;
+ specific_field.new_index = index2 - current_repeated_start2;
+ } else {
+ specific_field.index = index1 - current_repeated_start1;
+ specific_field.new_index = index2 - current_repeated_start2;
+ }
+
+ parent_field->push_back(specific_field);
+
+ switch (change_type) {
+ case ADDITION:
+ reporter_->ReportAdded(message1, message2, *parent_field);
+ ++index2;
+ break;
+ case DELETION:
+ reporter_->ReportDeleted(message1, message2, *parent_field);
+ ++index1;
+ break;
+ case MODIFICATION:
+ reporter_->ReportModified(message1, message2, *parent_field);
+ ++index1;
+ ++index2;
+ break;
+ case COMPARE_GROUPS:
+ if (!CompareUnknownFields(message1, message2,
+ fields1[index1].second->group(),
+ fields2[index2].second->group(),
+ parent_field)) {
+ if (reporter_ == NULL) return false;
+ is_different = true;
+ reporter_->ReportModified(message1, message2, *parent_field);
+ }
+ ++index1;
+ ++index2;
+ break;
+ case NO_CHANGE:
+ ++index1;
+ ++index2;
+ if (report_matches_) {
+ reporter_->ReportMatched(message1, message2, *parent_field);
+ }
+ }
+
+ parent_field->pop_back();
+ }
+
+ return !is_different;
+}
+
+namespace {
+
+// Find maximum bipartite matching using the argumenting path algorithm.
+class MaximumMatcher {
+ public:
+ typedef ResultCallback2<bool, int, int> NodeMatchCallback;
+ // MaximumMatcher takes ownership of the passed in callback and uses it to
+ // determine whether a node on the left side of the bipartial graph matches
+ // a node on the right side. count1 is the number of nodes on the left side
+ // of the graph and count2 to is the number of nodes on the right side.
+ // Every node is referred to using 0-based indices.
+ // If a maximum match is found, the result will be stored in match_list1 and
+ // match_list2. match_list1[i] == j means the i-th node on the left side is
+ // matched to the j-th node on the right side and match_list2[x] == y means
+ // the x-th node on the right side is matched to y-th node on the left side.
+ // match_list1[i] == -1 means the node is not matched. Same with match_list2.
+ MaximumMatcher(int count1, int count2, NodeMatchCallback* callback,
+ vector<int>* match_list1, vector<int>* match_list2);
+ // Find a maximum match and return the number of matched node pairs.
+ // If early_return is true, this method will return 0 immediately when it
+ // finds that not all nodes on the left side can be matched.
+ int FindMaximumMatch(bool early_return);
+ private:
+ // Determines whether the node on the left side of the bipartial graph
+ // matches the one on the right side.
+ bool Match(int left, int right);
+ // Find an argumenting path starting from the node v on the left side. If a
+ // path can be found, update match_list2_ to reflect the path and return
+ // true.
+ bool FindArgumentPathDFS(int v, vector<bool>* visited);
+
+ int count1_;
+ int count2_;
+ google::protobuf::scoped_ptr<NodeMatchCallback> match_callback_;
+ map<pair<int, int>, bool> cached_match_results_;
+ vector<int>* match_list1_;
+ vector<int>* match_list2_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MaximumMatcher);
+};
+
+MaximumMatcher::MaximumMatcher(int count1, int count2,
+ NodeMatchCallback* callback,
+ vector<int>* match_list1,
+ vector<int>* match_list2)
+ : count1_(count1), count2_(count2), match_callback_(callback),
+ match_list1_(match_list1), match_list2_(match_list2) {
+ match_list1_->assign(count1, -1);
+ match_list2_->assign(count2, -1);
+}
+
+int MaximumMatcher::FindMaximumMatch(bool early_return) {
+ int result = 0;
+ for (int i = 0; i < count1_; ++i) {
+ vector<bool> visited(count1_);
+ if (FindArgumentPathDFS(i, &visited)) {
+ ++result;
+ } else if (early_return) {
+ return 0;
+ }
+ }
+ // Backfill match_list1_ as we only filled match_list2_ when finding
+ // argumenting pathes.
+ for (int i = 0; i < count2_; ++i) {
+ if ((*match_list2_)[i] != -1) {
+ (*match_list1_)[(*match_list2_)[i]] = i;
+ }
+ }
+ return result;
+}
+
+bool MaximumMatcher::Match(int left, int right) {
+ pair<int, int> p(left, right);
+ map<pair<int, int>, bool>::iterator it = cached_match_results_.find(p);
+ if (it != cached_match_results_.end()) {
+ return it->second;
+ }
+ cached_match_results_[p] = match_callback_->Run(left, right);
+ return cached_match_results_[p];
+}
+
+bool MaximumMatcher::FindArgumentPathDFS(int v, vector<bool>* visited) {
+ (*visited)[v] = true;
+ // We try to match those un-matched nodes on the right side first. This is
+ // the step that the navie greedy matching algorithm uses. In the best cases
+ // where the greedy algorithm can find a maximum matching, we will always
+ // find a match in this step and the performance will be identical to the
+ // greedy algorithm.
+ for (int i = 0; i < count2_; ++i) {
+ int matched = (*match_list2_)[i];
+ if (matched == -1 && Match(v, i)) {
+ (*match_list2_)[i] = v;
+ return true;
+ }
+ }
+ // Then we try those already matched nodes and see if we can find an
+ // alternaive match for the node matched to them.
+ // The greedy algorithm will stop before this and fail to produce the
+ // correct result.
+ for (int i = 0; i < count2_; ++i) {
+ int matched = (*match_list2_)[i];
+ if (matched != -1 && Match(v, i)) {
+ if (!(*visited)[matched] && FindArgumentPathDFS(matched, visited)) {
+ (*match_list2_)[i] = v;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+bool MessageDifferencer::MatchRepeatedFieldIndices(
+ const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* repeated_field,
+ const vector<SpecificField>& parent_fields,
+ vector<int>* match_list1,
+ vector<int>* match_list2) {
+ const int count1 =
+ message1.GetReflection()->FieldSize(message1, repeated_field);
+ const int count2 =
+ message2.GetReflection()->FieldSize(message2, repeated_field);
+ const MapKeyComparator* key_comparator = GetMapKeyComparator(repeated_field);
+
+ match_list1->assign(count1, -1);
+ match_list2->assign(count2, -1);
+
+ SpecificField specific_field;
+ specific_field.field = repeated_field;
+
+ bool success = true;
+ // Find potential match if this is a special repeated field.
+ if (key_comparator != NULL || IsTreatedAsSet(repeated_field)) {
+ if (scope_ == PARTIAL) {
+ // When partial matching is enabled, Compare(a, b) && Compare(a, c)
+ // doesn't neccessarily imply Compare(b, c). Therefore a naive greedy
+ // algorithm will fail to find a maximum matching.
+ // Here we use the argumenting path algorithm.
+ MaximumMatcher::NodeMatchCallback* callback = NewPermanentCallback(
+ this, &MessageDifferencer::IsMatch, repeated_field, key_comparator,
+ &message1, &message2, parent_fields);
+ MaximumMatcher matcher(count1, count2, callback, match_list1,
+ match_list2);
+ // If diff info is not needed, we should end the matching process as
+ // soon as possible if not all items can be matched.
+ bool early_return = (reporter_ == NULL);
+ int match_count = matcher.FindMaximumMatch(early_return);
+ if (match_count != count1 && reporter_ == NULL) return false;
+ success = success && (match_count == count1);
+ } else {
+ for (int i = 0; i < count1; ++i) {
+ // Indicates any matched elements for this repeated field.
+ bool match = false;
+
+ specific_field.index = i;
+ specific_field.new_index = i;
+
+ for (int j = 0; j < count2; j++) {
+ if (match_list2->at(j) != -1) continue;
+ specific_field.index = i;
+ specific_field.new_index = j;
+
+ match = IsMatch(repeated_field, key_comparator,
+ &message1, &message2, parent_fields, i, j);
+
+ if (match) {
+ match_list1->at(specific_field.index) = specific_field.new_index;
+ match_list2->at(specific_field.new_index) = specific_field.index;
+ break;
+ }
+ }
+ if (!match && reporter_ == NULL) return false;
+ success = success && match;
+ }
+ }
+ } else {
+ // If this field should be treated as list, just label the match_list.
+ for (int i = 0; i < count1 && i < count2; i++) {
+ match_list1->at(i) = i;
+ match_list2->at(i) = i;
+ }
+ }
+
+ return success;
+}
+
+FieldComparator::ComparisonResult MessageDifferencer::GetFieldComparisonResult(
+ const Message& message1, const Message& message2,
+ const FieldDescriptor* field, int index1, int index2,
+ const FieldContext* field_context) {
+ FieldComparator* comparator = field_comparator_ != NULL ?
+ field_comparator_ : &default_field_comparator_;
+ return comparator->Compare(message1, message2, field,
+ index1, index2, field_context);
+}
+
+// ===========================================================================
+
+MessageDifferencer::Reporter::Reporter() { }
+MessageDifferencer::Reporter::~Reporter() {}
+
+// ===========================================================================
+
+MessageDifferencer::MapKeyComparator::MapKeyComparator() {}
+MessageDifferencer::MapKeyComparator::~MapKeyComparator() {}
+
+// ===========================================================================
+
+MessageDifferencer::IgnoreCriteria::IgnoreCriteria() {}
+MessageDifferencer::IgnoreCriteria::~IgnoreCriteria() {}
+
+// ===========================================================================
+
+// Note that the printer's delimiter is not used, because if we are given a
+// printer, we don't know its delimiter.
+MessageDifferencer::StreamReporter::StreamReporter(
+ io::ZeroCopyOutputStream* output) : printer_(new io::Printer(output, '$')),
+ delete_printer_(true),
+ report_modified_aggregates_(false) { }
+
+MessageDifferencer::StreamReporter::StreamReporter(
+ io::Printer* printer) : printer_(printer),
+ delete_printer_(false),
+ report_modified_aggregates_(false) { }
+
+MessageDifferencer::StreamReporter::~StreamReporter() {
+ if (delete_printer_) delete printer_;
+}
+
+void MessageDifferencer::StreamReporter::PrintPath(
+ const vector<SpecificField>& field_path, bool left_side) {
+ for (int i = 0; i < field_path.size(); ++i) {
+ if (i > 0) {
+ printer_->Print(".");
+ }
+
+ SpecificField specific_field = field_path[i];
+
+ if (specific_field.field != NULL) {
+ if (specific_field.field->is_extension()) {
+ printer_->Print("($name$)", "name",
+ specific_field.field->full_name());
+ } else {
+ printer_->PrintRaw(specific_field.field->name());
+ }
+ } else {
+ printer_->PrintRaw(SimpleItoa(specific_field.unknown_field_number));
+ }
+ if (left_side && specific_field.index >= 0) {
+ printer_->Print("[$name$]", "name", SimpleItoa(specific_field.index));
+ }
+ if (!left_side && specific_field.new_index >= 0) {
+ printer_->Print("[$name$]", "name", SimpleItoa(specific_field.new_index));
+ }
+ }
+}
+
+void MessageDifferencer::
+StreamReporter::PrintValue(const Message& message,
+ const vector<SpecificField>& field_path,
+ bool left_side) {
+ const SpecificField& specific_field = field_path.back();
+ const FieldDescriptor* field = specific_field.field;
+ if (field != NULL) {
+ string output;
+ int index = left_side ? specific_field.index : specific_field.new_index;
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ const Reflection* reflection = message.GetReflection();
+ const Message& field_message = field->is_repeated() ?
+ reflection->GetRepeatedMessage(message, field, index) :
+ reflection->GetMessage(message, field);
+ output = field_message.ShortDebugString();
+ if (output.empty()) {
+ printer_->Print("{ }");
+ } else {
+ printer_->Print("{ $name$ }", "name", output);
+ }
+ } else {
+ TextFormat::PrintFieldValueToString(message, field, index, &output);
+ printer_->PrintRaw(output);
+ }
+ } else {
+ const UnknownFieldSet* unknown_fields =
+ (left_side ?
+ specific_field.unknown_field_set1 :
+ specific_field.unknown_field_set2);
+ const UnknownField* unknown_field = &unknown_fields->field(
+ left_side ?
+ specific_field.unknown_field_index1 :
+ specific_field.unknown_field_index2);
+ PrintUnknownFieldValue(unknown_field);
+ }
+}
+
+void MessageDifferencer::
+StreamReporter::PrintUnknownFieldValue(const UnknownField* unknown_field) {
+ GOOGLE_CHECK(unknown_field != NULL) << " Cannot print NULL unknown_field.";
+
+ string output;
+ switch (unknown_field->type()) {
+ case UnknownField::TYPE_VARINT:
+ output = SimpleItoa(unknown_field->varint());
+ break;
+ case UnknownField::TYPE_FIXED32:
+ output = StrCat("0x", strings::Hex(unknown_field->fixed32(),
+ strings::ZERO_PAD_8));
+ break;
+ case UnknownField::TYPE_FIXED64:
+ output = StrCat("0x", strings::Hex(unknown_field->fixed64(),
+ strings::ZERO_PAD_16));
+ break;
+ case UnknownField::TYPE_LENGTH_DELIMITED:
+ output = StringPrintf("\"%s\"",
+ CEscape(unknown_field->length_delimited()).c_str());
+ break;
+ case UnknownField::TYPE_GROUP:
+ // TODO(kenton): Print the contents of the group like we do for
+ // messages. Requires an equivalent of ShortDebugString() for
+ // UnknownFieldSet.
+ output = "{ ... }";
+ break;
+ }
+ printer_->PrintRaw(output);
+}
+
+void MessageDifferencer::StreamReporter::Print(const string& str) {
+ printer_->Print(str.c_str());
+}
+
+void MessageDifferencer::StreamReporter::ReportAdded(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) {
+ printer_->Print("added: ");
+ PrintPath(field_path, false);
+ printer_->Print(": ");
+ PrintValue(message2, field_path, false);
+ printer_->Print("\n"); // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportDeleted(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) {
+ printer_->Print("deleted: ");
+ PrintPath(field_path, true);
+ printer_->Print(": ");
+ PrintValue(message1, field_path, true);
+ printer_->Print("\n"); // Print for newlines
+}
+
+void MessageDifferencer::StreamReporter::ReportModified(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) {
+ if (!report_modified_aggregates_ && field_path.back().field == NULL) {
+ if (field_path.back().unknown_field_type == UnknownField::TYPE_GROUP) {
+ // Any changes to the subfields have already been printed.
+ return;
+ }
+ } else if (!report_modified_aggregates_) {
+ if (field_path.back().field->cpp_type() ==
+ FieldDescriptor::CPPTYPE_MESSAGE) {
+ // Any changes to the subfields have already been printed.
+ return;
+ }
+ }
+
+ printer_->Print("modified: ");
+ PrintPath(field_path, true);
+ if (CheckPathChanged(field_path)) {
+ printer_->Print(" -> ");
+ PrintPath(field_path, false);
+ }
+ printer_->Print(": ");
+ PrintValue(message1, field_path, true);
+ printer_->Print(" -> ");
+ PrintValue(message2, field_path, false);
+ printer_->Print("\n"); // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportMoved(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) {
+ printer_->Print("moved: ");
+ PrintPath(field_path, true);
+ printer_->Print(" -> ");
+ PrintPath(field_path, false);
+ printer_->Print(" : ");
+ PrintValue(message1, field_path, true);
+ printer_->Print("\n"); // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportMatched(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) {
+ printer_->Print("matched: ");
+ PrintPath(field_path, true);
+ if (CheckPathChanged(field_path)) {
+ printer_->Print(" -> ");
+ PrintPath(field_path, false);
+ }
+ printer_->Print(" : ");
+ PrintValue(message1, field_path, true);
+ printer_->Print("\n"); // Print for newlines.
+}
+
+void MessageDifferencer::StreamReporter::ReportIgnored(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) {
+ printer_->Print("ignored: ");
+ PrintPath(field_path, true);
+ if (CheckPathChanged(field_path)) {
+ printer_->Print(" -> ");
+ PrintPath(field_path, false);
+ }
+ printer_->Print("\n"); // Print for newlines.
+}
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h
new file mode 100644
index 00000000..05548897
--- /dev/null
+++ b/src/google/protobuf/util/message_differencer.h
@@ -0,0 +1,817 @@
+// 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: jschorr@google.com (Joseph Schorr)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+//
+// This file defines static methods and classes for comparing Protocol
+// Messages.
+//
+// Aug. 2008: Added Unknown Fields Comparison for messages.
+// Aug. 2009: Added different options to compare repeated fields.
+// Apr. 2010: Moved field comparison to FieldComparator.
+
+#ifndef GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__
+#define GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <google/protobuf/descriptor.h> // FieldDescriptor
+#include <google/protobuf/message.h> // Message
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/util/field_comparator.h>
+
+namespace google {
+namespace protobuf {
+
+class DynamicMessageFactory;
+class FieldDescriptor;
+
+namespace io {
+class ZeroCopyOutputStream;
+class Printer;
+}
+
+namespace util {
+
+class FieldContext; // declared below MessageDifferencer
+
+// A basic differencer that can be used to determine
+// the differences between two specified Protocol Messages. If any differences
+// are found, the Compare method will return false, and any differencer reporter
+// specified via ReportDifferencesTo will have its reporting methods called (see
+// below for implementation of the report). Based off of the original
+// ProtocolDifferencer implementation in //net/proto/protocol-differencer.h
+// (Thanks Todd!).
+//
+// MessageDifferencer REQUIRES that compared messages be the same type, defined
+// as messages that share the same descriptor. If not, the behavior of this
+// class is undefined.
+//
+// People disagree on what MessageDifferencer should do when asked to compare
+// messages with different descriptors. Some people think it should always
+// return false. Others expect it to try to look for similar fields and
+// compare them anyway -- especially if the descriptors happen to be identical.
+// If we chose either of these behaviors, some set of people would find it
+// surprising, and could end up writing code expecting the other behavior
+// without realizing their error. Therefore, we forbid that usage.
+//
+// This class is implemented based on the proto2 reflection. The performance
+// should be good enough for normal usages. However, for places where the
+// performance is extremely sensitive, there are several alternatives:
+// - Comparing serialized string
+// Downside: false negatives (there are messages that are the same but their
+// serialized strings are different).
+// - Equals code generator by compiler plugin (net/proto2/contrib/equals_plugin)
+// Downside: more generated code; maintenance overhead for the additional rule
+// (must be in sync with the original proto_library).
+//
+// Note on handling of google.protobuf.Any: MessageDifferencer automatically
+// unpacks Any::value into a Message and compares its individual fields.
+// Messages encoded in a repeated Any cannot be compared using TreatAsMap.
+//
+//
+// Note on thread-safety: MessageDifferencer is *not* thread-safe. You need to
+// guard it with a lock to use the same MessageDifferencer instance from
+// multiple threads. Note that it's fine to call static comparison methods
+// (like MessageDifferencer::Equals) concurrently.
+class LIBPROTOBUF_EXPORT MessageDifferencer {
+ public:
+ // Determines whether the supplied messages are equal. Equality is defined as
+ // all fields within the two messages being set to the same value. Primitive
+ // fields and strings are compared by value while embedded messages/groups
+ // are compared as if via a recursive call. Use IgnoreField() and Compare()
+ // if some fields should be ignored in the comparison.
+ //
+ // This method REQUIRES that the two messages have the same
+ // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+ static bool Equals(const Message& message1, const Message& message2);
+
+ // Determines whether the supplied messages are equivalent. Equivalency is
+ // defined as all fields within the two messages having the same value. This
+ // differs from the Equals method above in that fields with default values
+ // are considered set to said value automatically. For details on how default
+ // values are defined for each field type, see http://shortn/_x2Gv6XFrWt.
+ // Also, Equivalent() ignores unknown fields. Use IgnoreField() and Compare()
+ // if some fields should be ignored in the comparison.
+ //
+ // This method REQUIRES that the two messages have the same
+ // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+ static bool Equivalent(const Message& message1, const Message& message2);
+
+ // Determines whether the supplied messages are approximately equal.
+ // Approximate equality is defined as all fields within the two messages
+ // being approximately equal. Primitive (non-float) fields and strings are
+ // compared by value, floats are compared using MathUtil::AlmostEquals() and
+ // embedded messages/groups are compared as if via a recursive call. Use
+ // IgnoreField() and Compare() if some fields should be ignored in the
+ // comparison.
+ //
+ // This method REQUIRES that the two messages have the same
+ // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+ static bool ApproximatelyEquals(const Message& message1,
+ const Message& message2);
+
+ // Determines whether the supplied messages are approximately equivalent.
+ // Approximate equivalency is defined as all fields within the two messages
+ // being approximately equivalent. As in
+ // MessageDifferencer::ApproximatelyEquals, primitive (non-float) fields and
+ // strings are compared by value, floats are compared using
+ // MathUtil::AlmostEquals() and embedded messages/groups are compared as if
+ // via a recursive call. However, fields with default values are considered
+ // set to said value, as per MessageDiffencer::Equivalent. Use IgnoreField()
+ // and Compare() if some fields should be ignored in the comparison.
+ //
+ // This method REQUIRES that the two messages have the same
+ // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+ static bool ApproximatelyEquivalent(const Message& message1,
+ const Message& message2);
+
+ // Identifies an individual field in a message instance. Used for field_path,
+ // below.
+ struct SpecificField {
+ // For known fields, "field" is filled in and "unknown_field_number" is -1.
+ // For unknown fields, "field" is NULL, "unknown_field_number" is the field
+ // number, and "unknown_field_type" is its type.
+ const FieldDescriptor* field;
+ int unknown_field_number;
+ UnknownField::Type unknown_field_type;
+
+ // If this a repeated field, "index" is the index within it. For unknown
+ // fields, this is the index of the field among all unknown fields of the
+ // same field number and type.
+ int index;
+
+ // If "field" is a repeated field which is being treated as a map or
+ // a set (see TreatAsMap() and TreatAsSet(), below), new_index indicates
+ // the index the position to which the element has moved. This only
+ // applies to ReportMoved() and (in the case of TreatAsMap())
+ // ReportModified(). In all other cases, "new_index" will have the same
+ // value as "index".
+ int new_index;
+
+ // For unknown fields, these are the pointers to the UnknownFieldSet
+ // containing the unknown fields. In certain cases (e.g. proto1's
+ // MessageSet, or nested groups of unknown fields), these may differ from
+ // the messages' internal UnknownFieldSets.
+ const UnknownFieldSet* unknown_field_set1;
+ const UnknownFieldSet* unknown_field_set2;
+
+ // For unknown fields, these are the index of the field within the
+ // UnknownFieldSets. One or the other will be -1 when
+ // reporting an addition or deletion.
+ int unknown_field_index1;
+ int unknown_field_index2;
+
+ SpecificField()
+ : field(NULL),
+ unknown_field_number(-1),
+ index(-1),
+ new_index(-1),
+ unknown_field_set1(NULL),
+ unknown_field_set2(NULL),
+ unknown_field_index1(-1),
+ unknown_field_index2(-1) {}
+ };
+
+ // Abstract base class from which all MessageDifferencer
+ // reporters derive. The five Report* methods below will be called when
+ // a field has been added, deleted, modified, moved, or matched. The third
+ // argument is a vector of FieldDescriptor pointers which describes the chain
+ // of fields that was taken to find the current field. For example, for a
+ // field found in an embedded message, the vector will contain two
+ // FieldDescriptors. The first will be the field of the embedded message
+ // itself and the second will be the actual field in the embedded message
+ // that was added/deleted/modified.
+ class LIBPROTOBUF_EXPORT Reporter {
+ public:
+ Reporter();
+ virtual ~Reporter();
+
+ // Reports that a field has been added into Message2.
+ virtual void ReportAdded(
+ const Message& message1, const Message& message2,
+ const vector<SpecificField>& field_path) = 0;
+
+ // Reports that a field has been deleted from Message1.
+ virtual void ReportDeleted(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) = 0;
+
+ // Reports that the value of a field has been modified.
+ virtual void ReportModified(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) = 0;
+
+ // Reports that a repeated field has been moved to another location. This
+ // only applies when using TreatAsSet or TreatAsMap() -- see below. Also
+ // note that for any given field, ReportModified and ReportMoved are
+ // mutually exclusive. If a field has been both moved and modified, then
+ // only ReportModified will be called.
+ virtual void ReportMoved(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) { }
+
+ // Reports that two fields match. Useful for doing side-by-side diffs.
+ // This function is mutually exclusive with ReportModified and ReportMoved.
+ // Note that you must call set_report_matches(true) before calling Compare
+ // to make use of this function.
+ virtual void ReportMatched(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) { }
+
+ // Reports that two fields would have been compared, but the
+ // comparison has been skipped because the field was marked as
+ // 'ignored' using IgnoreField(). This function is mutually
+ // exclusive with all the other Report() functions.
+ //
+ // The contract of ReportIgnored is slightly different than the
+ // other Report() functions, in that |field_path.back().index| is
+ // always equal to -1, even if the last field is repeated. This is
+ // because while the other Report() functions indicate where in a
+ // repeated field the action (Addition, Deletion, etc...)
+ // happened, when a repeated field is 'ignored', the differencer
+ // simply calls ReportIgnored on the repeated field as a whole and
+ // moves on without looking at its individual elements.
+ //
+ // Furthermore, ReportIgnored() does not indicate whether the
+ // fields were in fact equal or not, as Compare() does not inspect
+ // these fields at all. It is up to the Reporter to decide whether
+ // the fields are equal or not (perhaps with a second call to
+ // Compare()), if it cares.
+ virtual void ReportIgnored(
+ const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path) { }
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reporter);
+ };
+
+ // MapKeyComparator is used to determine if two elements have the same key
+ // when comparing elements of a repeated field as a map.
+ class LIBPROTOBUF_EXPORT MapKeyComparator {
+ public:
+ MapKeyComparator();
+ virtual ~MapKeyComparator();
+
+ // The first IsMatch without parent_fields is only for backward
+ // compatibility. New users should override the second one instead.
+ //
+ // Deprecated.
+ // TODO(ykzhu): remove this function.
+ virtual bool IsMatch(const Message& message1,
+ const Message& message2) const {
+ GOOGLE_CHECK(false) << "This function shouldn't get called";
+ return false;
+ }
+ virtual bool IsMatch(const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& parent_fields) const {
+ return IsMatch(message1, message2);
+ }
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapKeyComparator);
+ };
+
+ // Abstract base class from which all IgnoreCriteria derive.
+ // By adding IgnoreCriteria more complex ignore logic can be implemented.
+ // IgnoreCriteria are registed with AddIgnoreCriteria. For each compared
+ // field IsIgnored is called on each added IgnoreCriteria until one returns
+ // true or all return false.
+ // IsIgnored is called for fields where at least one side has a value.
+ class LIBPROTOBUF_EXPORT IgnoreCriteria {
+ public:
+ IgnoreCriteria();
+ virtual ~IgnoreCriteria();
+
+ // Returns true if the field should be ignored.
+ virtual bool IsIgnored(
+ const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* field,
+ const vector<SpecificField>& parent_fields) = 0;
+ };
+
+ // To add a Reporter, construct default here, then use ReportDifferencesTo or
+ // ReportDifferencesToString.
+ explicit MessageDifferencer();
+
+ ~MessageDifferencer();
+
+ enum MessageFieldComparison {
+ EQUAL, // Fields must be present in both messages
+ // for the messages to be considered the same.
+ EQUIVALENT, // Fields with default values are considered set
+ // for comparison purposes even if not explicitly
+ // set in the messages themselves. Unknown fields
+ // are ignored.
+ };
+
+ enum Scope {
+ FULL, // All fields of both messages are considered in the comparison.
+ PARTIAL // Only fields present in the first message are considered; fields
+ // set only in the second message will be skipped during
+ // comparison.
+ };
+
+ // DEPRECATED. Use FieldComparator::FloatComparison instead.
+ enum FloatComparison {
+ EXACT, // Floats and doubles are compared exactly.
+ APPROXIMATE // Floats and doubles are compared using the
+ // MathUtil::AlmostEquals method.
+ };
+
+ enum RepeatedFieldComparison {
+ AS_LIST, // Repeated fields are compared in order. Differing values at
+ // the same index are reported using ReportModified(). If the
+ // repeated fields have different numbers of elements, the
+ // unpaired elements are reported using ReportAdded() or
+ // ReportDeleted().
+ AS_SET, // Treat all the repeated fields as sets by default.
+ // See TreatAsSet(), as below.
+ };
+
+ // The elements of the given repeated field will be treated as a set for
+ // diffing purposes, so different orderings of the same elements will be
+ // considered equal. Elements which are present on both sides of the
+ // comparison but which have changed position will be reported with
+ // ReportMoved(). Elements which only exist on one side or the other are
+ // reported with ReportAdded() and ReportDeleted() regardless of their
+ // positions. ReportModified() is never used for this repeated field. If
+ // the only differences between the compared messages is that some fields
+ // have been moved, then the comparison returns true.
+ //
+ // If the scope of comparison is set to PARTIAL, then in addition to what's
+ // above, extra values added to repeated fields of the second message will
+ // not cause the comparison to fail.
+ //
+ // Note that set comparison is currently O(k * n^2) (where n is the total
+ // number of elements, and k is the average size of each element). In theory
+ // it could be made O(n * k) with a more complex hashing implementation. Feel
+ // free to contribute one if the current implementation is too slow for you.
+ // If partial matching is also enabled, the time complexity will be O(k * n^2
+ // + n^3) in which n^3 is the time complexity of the maximum matching
+ // algorithm.
+ //
+ // REQUIRES: field->is_repeated()
+ void TreatAsSet(const FieldDescriptor* field);
+
+ // The elements of the given repeated field will be treated as a map for
+ // diffing purposes, with |key| being the map key. Thus, elements with the
+ // same key will be compared even if they do not appear at the same index.
+ // Differences are reported similarly to TreatAsSet(), except that
+ // ReportModified() is used to report elements with the same key but
+ // different values. Note that if an element is both moved and modified,
+ // only ReportModified() will be called. As with TreatAsSet, if the only
+ // differences between the compared messages is that some fields have been
+ // moved, then the comparison returns true. See TreatAsSet for notes on
+ // performance.
+ //
+ // REQUIRES: field->is_repeated()
+ // REQUIRES: field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
+ // REQUIRES: key->containing_type() == field->message_type()
+ void TreatAsMap(const FieldDescriptor* field, const FieldDescriptor* key);
+ // Same as TreatAsMap except that this method will use multiple fields as
+ // the key in comparison. All specified fields in 'key_fields' should be
+ // present in the compared elements. Two elements will be treated as having
+ // the same key iff they have the same value for every specified field. There
+ // are two steps in the comparison process. The first one is key matching.
+ // Every element from one message will be compared to every element from
+ // the other message. Only fields in 'key_fields' are compared in this step
+ // to decide if two elements have the same key. The second step is value
+ // comparison. Those pairs of elements with the same key (with equal value
+ // for every field in 'key_fields') will be compared in this step.
+ // Time complexity of the first step is O(s * m * n ^ 2) where s is the
+ // average size of the fields specified in 'key_fields', m is the number of
+ // fields in 'key_fields' and n is the number of elements. If partial
+ // matching is enabled, an extra O(n^3) will be incured by the maximum
+ // matching algorithm. The second step is O(k * n) where k is the average
+ // size of each element.
+ void TreatAsMapWithMultipleFieldsAsKey(
+ const FieldDescriptor* field,
+ const vector<const FieldDescriptor*>& key_fields);
+ // Same as TreatAsMapWithMultipleFieldsAsKey, except that each of the field
+ // do not necessarily need to be a direct subfield. Each element in
+ // key_field_paths indicate a path from the message being compared, listing
+ // successive subfield to reach the key field.
+ //
+ // REQUIRES:
+ // for key_field_path in key_field_paths:
+ // key_field_path[0]->containing_type() == field->message_type()
+ // for i in [0, key_field_path.size() - 1):
+ // key_field_path[i+1]->containing_type() ==
+ // key_field_path[i]->message_type()
+ // key_field_path[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
+ // !key_field_path[i]->is_repeated()
+ void TreatAsMapWithMultipleFieldPathsAsKey(
+ const FieldDescriptor* field,
+ const vector<vector<const FieldDescriptor*> >& key_field_paths);
+
+ // Uses a custom MapKeyComparator to determine if two elements have the same
+ // key when comparing a repeated field as a map.
+ // The caller is responsible to delete the key_comparator.
+ // This method varies from TreatAsMapWithMultipleFieldsAsKey only in the
+ // first key matching step. Rather than comparing some specified fields, it
+ // will invoke the IsMatch method of the given 'key_comparator' to decide if
+ // two elements have the same key.
+ void TreatAsMapUsingKeyComparator(
+ const FieldDescriptor* field,
+ const MapKeyComparator* key_comparator);
+
+ // Add a custom ignore criteria that is evaluated in addition to the
+ // ignored fields added with IgnoreField.
+ // Takes ownership of ignore_criteria.
+ void AddIgnoreCriteria(IgnoreCriteria* ignore_criteria);
+
+ // Indicates that any field with the given descriptor should be
+ // ignored for the purposes of comparing two messages. This applies
+ // to fields nested in the message structure as well as top level
+ // ones. When the MessageDifferencer encounters an ignored field,
+ // ReportIgnored is called on the reporter, if one is specified.
+ //
+ // The only place where the field's 'ignored' status is not applied is when
+ // it is being used as a key in a field passed to TreatAsMap or is one of
+ // the fields passed to TreatAsMapWithMultipleFieldsAsKey.
+ // In this case it is compared in key matching but after that it's ignored
+ // in value comparison.
+ void IgnoreField(const FieldDescriptor* field);
+
+ // Sets the field comparator used to determine differences between protocol
+ // buffer fields. By default it's set to a DefaultFieldComparator instance.
+ // MessageDifferencer doesn't take ownership over the passed object.
+ // Note that this method must be called before Compare for the comparator to
+ // be used.
+ void set_field_comparator(FieldComparator* comparator);
+
+ // DEPRECATED. Pass a DefaultFieldComparator instance instead.
+ // Sets the fraction and margin for the float comparison of a given field.
+ // Uses MathUtil::WithinFractionOrMargin to compare the values.
+ // NOTE: this method does nothing if differencer's field comparator has been
+ // set to a custom object.
+ //
+ // REQUIRES: field->cpp_type == FieldDescriptor::CPPTYPE_DOUBLE or
+ // field->cpp_type == FieldDescriptor::CPPTYPE_FLOAT
+ // REQUIRES: float_comparison_ == APPROXIMATE
+ void SetFractionAndMargin(const FieldDescriptor* field, double fraction,
+ double margin);
+
+ // Sets the type of comparison (as defined in the MessageFieldComparison
+ // enumeration above) that is used by this differencer when determining how
+ // to compare fields in messages.
+ void set_message_field_comparison(MessageFieldComparison comparison);
+
+ // Tells the differencer whether or not to report matches. This method must
+ // be called before Compare. The default for a new differencer is false.
+ void set_report_matches(bool report_matches) {
+ report_matches_ = report_matches;
+ }
+
+ // Sets the scope of the comparison (as defined in the Scope enumeration
+ // above) that is used by this differencer when determining which fields to
+ // compare between the messages.
+ void set_scope(Scope scope);
+
+ // Returns the current scope used by this differencer.
+ Scope scope();
+
+ // DEPRECATED. Pass a DefaultFieldComparator instance instead.
+ // Sets the type of comparison (as defined in the FloatComparison enumeration
+ // above) that is used by this differencer when comparing float (and double)
+ // fields in messages.
+ // NOTE: this method does nothing if differencer's field comparator has been
+ // set to a custom object.
+ void set_float_comparison(FloatComparison comparison);
+
+ // Sets the type of comparison for repeated field (as defined in the
+ // RepeatedFieldComparison enumeration above) that is used by this
+ // differencer when compare repeated fields in messages.
+ void set_repeated_field_comparison(RepeatedFieldComparison comparison);
+
+ // Compares the two specified messages, returning true if they are the same,
+ // false otherwise. If this method returns false, any changes between the
+ // two messages will be reported if a Reporter was specified via
+ // ReportDifferencesTo (see also ReportDifferencesToString).
+ //
+ // This method REQUIRES that the two messages have the same
+ // Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
+ bool Compare(const Message& message1, const Message& message2);
+
+ // Same as above, except comparing only the list of fields specified by the
+ // two vectors of FieldDescriptors.
+ bool CompareWithFields(const Message& message1, const Message& message2,
+ const vector<const FieldDescriptor*>& message1_fields,
+ const vector<const FieldDescriptor*>& message2_fields);
+
+ // Automatically creates a reporter that will output the differences
+ // found (if any) to the specified output string pointer. Note that this
+ // method must be called before Compare.
+ void ReportDifferencesToString(string* output);
+
+ // Tells the MessageDifferencer to report differences via the specified
+ // reporter. Note that this method must be called before Compare for
+ // the reporter to be used. It is the responsibility of the caller to delete
+ // this object.
+ // If the provided pointer equals NULL, the MessageDifferencer stops reporting
+ // differences to any previously set reporters or output strings.
+ void ReportDifferencesTo(Reporter* reporter);
+
+ // An implementation of the MessageDifferencer Reporter that outputs
+ // any differences found in human-readable form to the supplied
+ // ZeroCopyOutputStream or Printer. If a printer is used, the delimiter
+ // *must* be '$'.
+ class LIBPROTOBUF_EXPORT StreamReporter : public Reporter {
+ public:
+ explicit StreamReporter(io::ZeroCopyOutputStream* output);
+ explicit StreamReporter(io::Printer* printer); // delimiter '$'
+ virtual ~StreamReporter();
+
+ // When set to true, the stream reporter will also output aggregates nodes
+ // (i.e. messages and groups) whose subfields have been modified. When
+ // false, will only report the individual subfields. Defaults to false.
+ void set_report_modified_aggregates(bool report) {
+ report_modified_aggregates_ = report;
+ }
+
+ // The following are implementations of the methods described above.
+ virtual void ReportAdded(const Message& message1, const Message& message2,
+ const vector<SpecificField>& field_path);
+
+ virtual void ReportDeleted(const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path);
+
+ virtual void ReportModified(const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path);
+
+ virtual void ReportMoved(const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path);
+
+ virtual void ReportMatched(const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path);
+
+ virtual void ReportIgnored(const Message& message1,
+ const Message& message2,
+ const vector<SpecificField>& field_path);
+
+ protected:
+ // Prints the specified path of fields to the buffer.
+ virtual void PrintPath(const vector<SpecificField>& field_path,
+ bool left_side);
+
+ // Prints the value of fields to the buffer. left_side is true if the
+ // given message is from the left side of the comparison, false if it
+ // was the right. This is relevant only to decide whether to follow
+ // unknown_field_index1 or unknown_field_index2 when an unknown field
+ // is encountered in field_path.
+ virtual void PrintValue(const Message& message,
+ const vector<SpecificField>& field_path,
+ bool left_side);
+
+ // Prints the specified path of unknown fields to the buffer.
+ virtual void PrintUnknownFieldValue(const UnknownField* unknown_field);
+
+ // Just print a string
+ void Print(const string& str);
+
+ private:
+ io::Printer* printer_;
+ bool delete_printer_;
+ bool report_modified_aggregates_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StreamReporter);
+ };
+
+ private:
+ // A MapKeyComparator to be used in TreatAsMapUsingKeyComparator.
+ // Implementation of this class needs to do field value comparison which
+ // relies on some private methods of MessageDifferencer. That's why this
+ // class is declared as a nested class of MessageDifferencer.
+ class MultipleFieldsMapKeyComparator;
+ // Returns true if field1's number() is less than field2's.
+ static bool FieldBefore(const FieldDescriptor* field1,
+ const FieldDescriptor* field2);
+
+ // Combine the two lists of fields into the combined_fields output vector.
+ // All fields present in both lists will always be included in the combined
+ // list. Fields only present in one of the lists will only appear in the
+ // combined list if the corresponding fields_scope option is set to FULL.
+ void CombineFields(const vector<const FieldDescriptor*>& fields1,
+ Scope fields1_scope,
+ const vector<const FieldDescriptor*>& fields2,
+ Scope fields2_scope,
+ vector<const FieldDescriptor*>* combined_fields);
+
+ // Internal version of the Compare method which performs the actual
+ // comparison. The parent_fields vector is a vector containing field
+ // descriptors of all fields accessed to get to this comparison operation
+ // (i.e. if the current message is an embedded message, the parent_fields
+ // vector will contain the field that has this embedded message).
+ bool Compare(const Message& message1, const Message& message2,
+ vector<SpecificField>* parent_fields);
+
+ // Compares all the unknown fields in two messages.
+ bool CompareUnknownFields(const Message& message1, const Message& message2,
+ const google::protobuf::UnknownFieldSet&,
+ const google::protobuf::UnknownFieldSet&,
+ vector<SpecificField>* parent_fields);
+
+ // Compares the specified messages for the requested field lists. The field
+ // lists are modified depending on comparison settings, and then passed to
+ // CompareWithFieldsInternal.
+ bool CompareRequestedFieldsUsingSettings(
+ const Message& message1, const Message& message2,
+ const vector<const FieldDescriptor*>& message1_fields,
+ const vector<const FieldDescriptor*>& message2_fields,
+ vector<SpecificField>* parent_fields);
+
+ // Compares the specified messages with the specified field lists.
+ bool CompareWithFieldsInternal(
+ const Message& message1, const Message& message2,
+ const vector<const FieldDescriptor*>& message1_fields,
+ const vector<const FieldDescriptor*>& message2_fields,
+ vector<SpecificField>* parent_fields);
+
+ // Compares the repeated fields, and report the error.
+ bool CompareRepeatedField(const Message& message1, const Message& message2,
+ const FieldDescriptor* field,
+ vector<SpecificField>* parent_fields);
+
+ // Shorthand for CompareFieldValueUsingParentFields with NULL parent_fields.
+ bool CompareFieldValue(const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* field,
+ int index1,
+ int index2);
+
+ // Compares the specified field on the two messages, returning
+ // true if they are the same, false otherwise. For repeated fields,
+ // this method only compares the value in the specified index. This method
+ // uses Compare functions to recurse into submessages.
+ // The parent_fields vector is used in calls to a Reporter instance calls.
+ // It can be NULL, in which case the MessageDifferencer will create new
+ // list of parent messages if it needs to recursively compare the given field.
+ // To avoid confusing users you should not set it to NULL unless you modified
+ // Reporter to handle the change of parent_fields correctly.
+ bool CompareFieldValueUsingParentFields(const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* field,
+ int index1,
+ int index2,
+ vector<SpecificField>* parent_fields);
+
+ // Compares the specified field on the two messages, returning comparison
+ // result, as returned by appropriate FieldComparator.
+ FieldComparator::ComparisonResult GetFieldComparisonResult(
+ const Message& message1, const Message& message2,
+ const FieldDescriptor* field, int index1, int index2,
+ const FieldContext* field_context);
+
+ // Check if the two elements in the repeated field are match to each other.
+ // if the key_comprator is NULL, this function returns true when the two
+ // elements are equal.
+ bool IsMatch(const FieldDescriptor* repeated_field,
+ const MapKeyComparator* key_comparator,
+ const Message* message1, const Message* message2,
+ const vector<SpecificField>& parent_fields,
+ int index1, int index2);
+
+ // Returns true when this repeated field has been configured to be treated
+ // as a set.
+ bool IsTreatedAsSet(const FieldDescriptor* field);
+
+ // Returns true when this repeated field is to be compared as a subset, ie.
+ // has been configured to be treated as a set or map and scope is set to
+ // PARTIAL.
+ bool IsTreatedAsSubset(const FieldDescriptor* field);
+
+ // Returns true if this field is to be ignored when this
+ // MessageDifferencer compares messages.
+ bool IsIgnored(
+ const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* field,
+ const vector<SpecificField>& parent_fields);
+
+ // Returns MapKeyComparator* when this field has been configured to
+ // be treated as a map. If not, returns NULL.
+ const MapKeyComparator* GetMapKeyComparator(const FieldDescriptor* field);
+
+ // Attempts to match indices of a repeated field, so that the contained values
+ // match. Clears output vectors and sets their values to indices of paired
+ // messages, ie. if message1[0] matches message2[1], then match_list1[0] == 1
+ // and match_list2[1] == 0. The unmatched indices are indicated by -1.
+ // This method returns false if the match failed. However, it doesn't mean
+ // that the comparison succeeds when this method returns true (you need to
+ // double-check in this case).
+ bool MatchRepeatedFieldIndices(const Message& message1,
+ const Message& message2,
+ const FieldDescriptor* repeated_field,
+ const vector<SpecificField>& parent_fields,
+ vector<int>* match_list1,
+ vector<int>* match_list2);
+
+ // If "any" is of type google.protobuf.Any, extract its payload using
+ // DynamicMessageFactory and store in "data".
+ bool UnpackAny(const Message& any, google::protobuf::scoped_ptr<Message>* data);
+
+ // Checks if index is equal to new_index in all the specific fields.
+ static bool CheckPathChanged(const vector<SpecificField>& parent_fields);
+
+ // Defines a map between field descriptors and their MapKeyComparators.
+ // Used for repeated fields when they are configured as TreatAsMap.
+ typedef map<const FieldDescriptor*,
+ const MapKeyComparator*> FieldKeyComparatorMap;
+
+ // Defines a set to store field descriptors. Used for repeated fields when
+ // they are configured as TreatAsSet.
+ typedef set<const FieldDescriptor*> FieldSet;
+
+ Reporter* reporter_;
+ DefaultFieldComparator default_field_comparator_;
+ FieldComparator* field_comparator_;
+ MessageFieldComparison message_field_comparison_;
+ Scope scope_;
+ RepeatedFieldComparison repeated_field_comparison_;
+
+ FieldSet set_fields_;
+ // Keeps track of MapKeyComparators that are created within
+ // MessageDifferencer. These MapKeyComparators should be deleted
+ // before MessageDifferencer is destroyed.
+ // When TreatAsMap or TreatAsMapWithMultipleFieldsAsKey is called, we don't
+ // store the supplied FieldDescriptors directly. Instead, a new
+ // MapKeyComparator is created for comparison purpose.
+ vector<MapKeyComparator*> owned_key_comparators_;
+ FieldKeyComparatorMap map_field_key_comparator_;
+ vector<IgnoreCriteria*> ignore_criteria_;
+
+ FieldSet ignored_fields_;
+
+ bool compare_unknown_fields_;
+ bool report_matches_;
+
+ string* output_string_;
+
+ google::protobuf::scoped_ptr<DynamicMessageFactory> dynamic_message_factory_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageDifferencer);
+};
+
+// This class provides extra information to the FieldComparator::Compare
+// function.
+class LIBPROTOBUF_EXPORT FieldContext {
+ public:
+ explicit FieldContext(
+ vector<MessageDifferencer::SpecificField>* parent_fields)
+ : parent_fields_(parent_fields) {}
+
+ vector<MessageDifferencer::SpecificField>* parent_fields() const {
+ return parent_fields_;
+ }
+
+ private:
+ vector<MessageDifferencer::SpecificField>* parent_fields_;
+};
+
+}
+}
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__
diff --git a/src/google/protobuf/util/message_differencer_unittest.cc b/src/google/protobuf/util/message_differencer_unittest.cc
new file mode 100755
index 00000000..bd19f695
--- /dev/null
+++ b/src/google/protobuf/util/message_differencer_unittest.cc
@@ -0,0 +1,3132 @@
+// 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: jschorr@google.com (Joseph Schorr)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+//
+// TODO(ksroka): Move some of these tests to field_comparator_test.cc.
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+
+#include <google/protobuf/util/field_comparator.h>
+#include <google/protobuf/util/message_differencer.h>
+#include <google/protobuf/util/message_differencer_unittest.pb.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/any_test.pb.h>
+#include <google/protobuf/map_unittest.pb.h>
+#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/map_test_util.h>
+#include <google/protobuf/test_util.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/strutil.h>
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+
+namespace {
+
+
+const FieldDescriptor* GetFieldDescriptor(
+ const Message& message, const string& field_name) {
+ vector<string> field_path =
+ Split(field_name, ".", true);
+ const Descriptor* descriptor = message.GetDescriptor();
+ const FieldDescriptor* field = NULL;
+ for (int i = 0; i < field_path.size(); i++) {
+ field = descriptor->FindFieldByName(field_path[i]);
+ descriptor = field->message_type();
+ }
+ return field;
+}
+
+void ExpectEqualsWithDifferencer(util::MessageDifferencer* differencer,
+ const Message& msg1,
+ const Message& msg2) {
+ differencer->set_scope(util::MessageDifferencer::FULL);
+ EXPECT_TRUE(differencer->Compare(msg1, msg2));
+
+ differencer->set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer->Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicEqualityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicInequalityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.set_optional_int32(-1);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldInequalityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.add_repeated_int32(-1);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, MapFieldEqualityTest) {
+ // Create the testing protos
+ unittest::TestMap msg1;
+ unittest::TestMap msg2;
+
+ MapTestUtil::MapReflectionTester tester(unittest::TestMap::descriptor());
+ tester.SetMapFieldsViaReflection(&msg1);
+ tester.SetMapFieldsViaReflection(&msg2);
+ tester.SwapMapsViaReflection(&msg1);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicPartialEqualityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, PartialEqualityTestExtraField) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.clear_optional_int32();
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, PartialEqualityTestSkipRequiredField) {
+ // Create the testing protos
+ unittest::TestRequired msg1;
+ unittest::TestRequired msg2;
+
+ msg1.set_a(401);
+ msg2.set_a(401);
+ msg2.set_b(402);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicPartialInequalityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.set_optional_int32(-1);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, PartialInequalityMissingFieldTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg2.clear_optional_int32();
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldPartialInequalityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.add_repeated_int32(-1);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicEquivalencyTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::Equivalent(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, EquivalencyNotEqualTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.clear_optional_int32();
+ msg2.set_optional_int32(0);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+ EXPECT_TRUE(util::MessageDifferencer::Equivalent(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicInequivalencyTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.set_optional_int32(-1);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equivalent(msg1, msg2));
+}
+
+
+TEST(MessageDifferencerTest, BasicEquivalencyNonSetTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::Equivalent(msg1, msg2));
+}
+
+
+TEST(MessageDifferencerTest, BasicInequivalencyNonSetTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ msg1.set_optional_int32(-1);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equivalent(msg1, msg2));
+}
+
+
+TEST(MessageDifferencerTest, BasicPartialEquivalencyTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, PartialEquivalencyNotEqualTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.set_optional_int32(0);
+ msg2.clear_optional_int32();
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, PartialEquivalencyTestExtraField) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.clear_optional_int32();
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, PartialEquivalencyTestSkipRequiredField) {
+ // Create the testing protos
+ unittest::TestRequired msg1;
+ unittest::TestRequired msg2;
+
+ msg1.set_a(401);
+ msg2.set_a(401);
+ msg2.set_b(402);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicPartialInequivalencyTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ msg1.set_optional_int32(-1);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicPartialEquivalencyNonSetTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicPartialInequivalencyNonSetTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ msg1.set_optional_int32(-1);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, ApproximateEqualityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::ApproximatelyEquals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, ApproximateModifiedEqualityTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ const float v1 = 2.300005f;
+ const float v2 = 2.300006f;
+ msg1.set_optional_float(v1);
+ msg2.set_optional_float(v2);
+
+ // Compare
+ ASSERT_NE(v1, v2) << "Should not be the same: " << v1 << ", " << v2;
+ ASSERT_FLOAT_EQ(v1, v2) << "Should be approx. equal: " << v1 << ", " << v2;
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+ EXPECT_TRUE(util::MessageDifferencer::ApproximatelyEquals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, ApproximateEquivalencyTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::ApproximatelyEquivalent(msg1,
+ msg2));
+}
+
+TEST(MessageDifferencerTest, ApproximateModifiedEquivalencyTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Modify the approximateness requirement
+ const float v1 = 2.300005f;
+ const float v2 = 2.300006f;
+ msg1.set_optional_float(v1);
+ msg2.set_optional_float(v2);
+
+ // Compare
+ ASSERT_NE(v1, v2) << "Should not be the same: " << v1 << ", " << v2;
+ ASSERT_FLOAT_EQ(v1, v2) << "Should be approx. equal: " << v1 << ", " << v2;
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+ EXPECT_TRUE(util::MessageDifferencer::ApproximatelyEquivalent(msg1,
+ msg2));
+
+ // Modify the equivalency requirement too
+ msg1.clear_optional_int32();
+ msg2.set_optional_int32(0);
+
+ // Compare. Now should only pass on ApproximatelyEquivalent
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+ EXPECT_FALSE(util::MessageDifferencer::Equivalent(msg1, msg2));
+ EXPECT_FALSE(util::MessageDifferencer::ApproximatelyEquals(msg1, msg2));
+ EXPECT_TRUE(util::MessageDifferencer::ApproximatelyEquivalent(msg1,
+ msg2));
+}
+
+TEST(MessageDifferencerTest, ApproximateInequivalencyTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Should fail on equivalency
+ msg1.set_optional_int32(-1);
+ EXPECT_FALSE(util::MessageDifferencer::ApproximatelyEquivalent(msg1,
+ msg2));
+
+ // Make these fields the same again.
+ msg1.set_optional_int32(0);
+ msg2.set_optional_int32(0);
+ EXPECT_TRUE(util::MessageDifferencer::ApproximatelyEquivalent(msg1,
+ msg2));
+
+ // Should fail on approximate equality check
+ const float v1 = 2.3f;
+ const float v2 = 9.3f;
+ msg1.set_optional_float(v1);
+ msg2.set_optional_float(v2);
+ EXPECT_FALSE(util::MessageDifferencer::ApproximatelyEquivalent(msg1,
+ msg2));
+}
+
+TEST(MessageDifferencerTest, WithinFractionOrMarginFloatTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Should fail on approximate equality check
+ const float v1 = 100.0f;
+ const float v2 = 109.9f;
+ msg1.set_optional_float(v1);
+ msg2.set_optional_float(v2);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ const FieldDescriptor* fd =
+ msg1.GetDescriptor()->FindFieldByName("optional_float");
+
+ // Set float comparison to exact, margin and fraction value should not matter.
+ differencer.set_float_comparison(util::MessageDifferencer::EXACT);
+ // Set margin for float comparison.
+ differencer.SetFractionAndMargin(fd, 0.0, 10.0);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Margin and fraction float comparison is activated when float comparison is
+ // set to approximate.
+ differencer.set_float_comparison(util::MessageDifferencer::APPROXIMATE);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Test out float comparison with fraction.
+ differencer.SetFractionAndMargin(fd, 0.2, 0.0);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Should fail since the fraction is smaller than error.
+ differencer.SetFractionAndMargin(fd, 0.01, 0.0);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Should pass if either fraction or margin are satisfied.
+ differencer.SetFractionAndMargin(fd, 0.01, 10.0);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Make sure that the margin and fraction only affects the field that it was
+ // set for.
+ msg1.set_default_float(v1);
+ msg2.set_default_float(v2);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ msg1.set_default_float(v1);
+ msg2.set_default_float(v1);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, WithinFractionOrMarginDoubleTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Should fail on approximate equality check
+ const double v1 = 100.0;
+ const double v2 = 109.9;
+ msg1.set_optional_double(v1);
+ msg2.set_optional_double(v2);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Set comparison to exact, margin and fraction value should not matter.
+ differencer.set_float_comparison(util::MessageDifferencer::EXACT);
+ // Set margin for float comparison.
+ const FieldDescriptor* fd =
+ msg1.GetDescriptor()->FindFieldByName("optional_double");
+ differencer.SetFractionAndMargin(fd, 0.0, 10.0);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Margin and fraction comparison is activated when float comparison is
+ // set to approximate.
+ differencer.set_float_comparison(util::MessageDifferencer::APPROXIMATE);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Test out comparison with fraction.
+ differencer.SetFractionAndMargin(fd, 0.2, 0.0);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Should fail since the fraction is smaller than error.
+ differencer.SetFractionAndMargin(fd, 0.01, 0.0);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Should pass if either fraction or margin are satisfied.
+ differencer.SetFractionAndMargin(fd, 0.01, 10.0);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Make sure that the margin and fraction only affects the field that it was
+ // set for.
+ msg1.set_default_double(v1);
+ msg2.set_default_double(v2);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ msg1.set_default_double(v1);
+ msg2.set_default_double(v1);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, WithinDefaultFractionOrMarginDoubleTest) {
+ // Create the testing protos
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ // Should fail on approximate equality check
+ const double v1 = 100.0;
+ const double v2 = 109.9;
+ msg1.set_optional_double(v1);
+ msg2.set_optional_double(v2);
+
+ util::MessageDifferencer differencer;
+
+ // Compare
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Set up a custom field comparitor, with a default fraction and margin for
+ // float and double comparison.
+ util::DefaultFieldComparator field_comparitor;
+ field_comparitor.SetDefaultFractionAndMargin(0.0, 10.0);
+ differencer.set_field_comparator(&field_comparitor);
+
+ // Set comparison to exact, margin and fraction value should not matter.
+ field_comparitor.set_float_comparison(util::DefaultFieldComparator::EXACT);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Margin and fraction comparison is activated when float comparison is
+ // set to approximate.
+ field_comparitor.set_float_comparison(
+ util::DefaultFieldComparator::APPROXIMATE);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Test out comparison with fraction.
+ field_comparitor.SetDefaultFractionAndMargin(0.2, 0.0);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Should fail since the fraction is smaller than error.
+ field_comparitor.SetDefaultFractionAndMargin(0.01, 0.0);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Should pass if either fraction or margin are satisfied.
+ field_comparitor.SetDefaultFractionAndMargin(0.01, 10.0);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Make sure that the default margin and fraction affects all fields
+ msg1.set_default_double(v1);
+ msg2.set_default_double(v2);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicFieldOrderingsTest) {
+ // Create the testing protos
+ unittest::TestFieldOrderings msg1;
+ unittest::TestFieldOrderings msg2;
+
+ TestUtil::SetAllFieldsAndExtensions(&msg1);
+ TestUtil::SetAllFieldsAndExtensions(&msg2);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+
+TEST(MessageDifferencerTest, BasicFieldOrderingInequalityTest) {
+ // Create the testing protos
+ unittest::TestFieldOrderings msg1;
+ unittest::TestFieldOrderings msg2;
+
+ TestUtil::SetAllFieldsAndExtensions(&msg1);
+ TestUtil::SetAllFieldsAndExtensions(&msg2);
+
+ msg1.set_my_float(15.00);
+ msg2.set_my_float(16.00);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, BasicExtensionTest) {
+ // Create the testing protos
+ unittest::TestAllExtensions msg1;
+ unittest::TestAllExtensions msg2;
+
+ TestUtil::SetAllExtensions(&msg1);
+ TestUtil::SetAllExtensions(&msg2);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+
+TEST(MessageDifferencerTest, BasicExtensionInequalityTest) {
+ // Create the testing protos
+ unittest::TestAllExtensions msg1;
+ unittest::TestAllExtensions msg2;
+
+ TestUtil::SetAllExtensions(&msg1);
+ TestUtil::SetAllExtensions(&msg2);
+
+ msg1.SetExtension(unittest::optional_int32_extension, 101);
+ msg2.SetExtension(unittest::optional_int32_extension, 102);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, OneofTest) {
+ // Create the testing protos
+ unittest::TestOneof2 msg1;
+ unittest::TestOneof2 msg2;
+
+ TestUtil::SetOneof1(&msg1);
+ TestUtil::SetOneof1(&msg2);
+
+ // Compare
+ EXPECT_TRUE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, OneofInequalityTest) {
+ // Create the testing protos
+ unittest::TestOneof2 msg1;
+ unittest::TestOneof2 msg2;
+
+ TestUtil::SetOneof1(&msg1);
+ TestUtil::SetOneof2(&msg2);
+
+ // Compare
+ EXPECT_FALSE(util::MessageDifferencer::Equals(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, UnknownFieldPartialEqualTest) {
+ unittest::TestEmptyMessage empty1;
+ unittest::TestEmptyMessage empty2;
+
+ UnknownFieldSet* unknown1 = empty1.mutable_unknown_fields();
+ UnknownFieldSet* unknown2 = empty2.mutable_unknown_fields();
+
+ unknown1->AddVarint(243, 122);
+ unknown1->AddLengthDelimited(245, "abc");
+ unknown1->AddGroup(246)->AddFixed32(248, 1);
+ unknown1->mutable_field(2)->mutable_group()->AddFixed32(248, 2);
+
+ unknown2->AddVarint(243, 122);
+ unknown2->AddLengthDelimited(245, "abc");
+ unknown2->AddGroup(246)->AddFixed32(248, 1);
+ unknown2->mutable_field(2)->mutable_group()->AddFixed32(248, 2);
+
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(empty1, empty2));
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsEqualityAllTest) {
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ vector<const FieldDescriptor*> fields1;
+ vector<const FieldDescriptor*> fields2;
+ msg1.GetReflection()->ListFields(msg1, &fields1);
+ msg2.GetReflection()->ListFields(msg2, &fields2);
+
+ util::MessageDifferencer differencer;
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2, fields1, fields2));
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsInequalityAllTest) {
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+
+ vector<const FieldDescriptor*> fields1;
+ vector<const FieldDescriptor*> fields2;
+ msg1.GetReflection()->ListFields(msg1, &fields1);
+ msg2.GetReflection()->ListFields(msg2, &fields2);
+
+ util::MessageDifferencer differencer;
+ EXPECT_FALSE(differencer.CompareWithFields(msg1, msg2, fields1, fields2));
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsEmptyListAlwaysSucceeds) {
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+
+ vector<const FieldDescriptor*> empty_fields;
+
+ util::MessageDifferencer differencer;
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2,
+ empty_fields, empty_fields));
+
+ TestUtil::SetAllFields(&msg2);
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2,
+ empty_fields, empty_fields));
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsCompareWithSelf) {
+ unittest::TestAllTypes msg1;
+ TestUtil::SetAllFields(&msg1);
+
+ vector<const FieldDescriptor*> fields;
+ msg1.GetReflection()->ListFields(msg1, &fields);
+
+ util::MessageDifferencer differencer;
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg1, fields, fields));
+
+ {
+ // Compare with a subset of fields.
+ vector<const FieldDescriptor*> compare_fields;
+ for (int i = 0; i < fields.size(); ++i) {
+ if (i % 2 == 0) {
+ compare_fields.push_back(fields[i]);
+ }
+ }
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg1,
+ compare_fields, compare_fields));
+ }
+ {
+ // Specify a different set of fields to compare, even though we're using the
+ // same message. This should fail, since we are explicitly saying that the
+ // set of fields are different.
+ vector<const FieldDescriptor*> compare_fields1;
+ vector<const FieldDescriptor*> compare_fields2;
+ for (int i = 0; i < fields.size(); ++i) {
+ if (i % 2 == 0) {
+ compare_fields1.push_back(fields[i]);
+ } else {
+ compare_fields2.push_back(fields[i]);
+ }
+ }
+ EXPECT_FALSE(differencer.CompareWithFields(
+ msg1, msg1, compare_fields1, compare_fields2));
+ }
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsEqualityAllShuffledTest) {
+ // This is a public function, so make sure there are no assumptions about the
+ // list of fields. Randomly shuffle them to make sure that they are properly
+ // ordered for comparison.
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ vector<const FieldDescriptor*> fields1;
+ vector<const FieldDescriptor*> fields2;
+ msg1.GetReflection()->ListFields(msg1, &fields1);
+ msg2.GetReflection()->ListFields(msg2, &fields2);
+
+ std::random_shuffle(fields1.begin(), fields1.end());
+ std::random_shuffle(fields2.begin(), fields2.end());
+
+ util::MessageDifferencer differencer;
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2, fields1, fields2));
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsSubsetEqualityTest) {
+ // Specify a set of fields to compare. All the fields are equal.
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ vector<const FieldDescriptor*> fields1;
+ msg1.GetReflection()->ListFields(msg1, &fields1);
+
+ vector<const FieldDescriptor*> compare_fields;
+ // Only compare the field descriptors with even indices.
+ for (int i = 0; i < fields1.size(); ++i) {
+ if (i % 2 == 0) {
+ compare_fields.push_back(fields1[i]);
+ }
+ }
+
+ util::MessageDifferencer differencer;
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2,
+ compare_fields, compare_fields));
+}
+
+TEST(MessageDifferencerTest,
+ SpecifiedFieldsSubsetIgnoresOtherFieldDifferencesTest) {
+ // Specify a set of fields to compare, but clear all the other fields in one
+ // of the messages. This should fail a regular compare, but CompareWithFields
+ // should succeed.
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ vector<const FieldDescriptor*> fields1;
+ const Reflection* reflection = msg1.GetReflection();
+ reflection->ListFields(msg1, &fields1);
+
+ vector<const FieldDescriptor*> compare_fields;
+ // Only compare the field descriptors with even indices.
+ for (int i = 0; i < fields1.size(); ++i) {
+ if (i % 2 == 0) {
+ compare_fields.push_back(fields1[i]);
+ } else {
+ reflection->ClearField(&msg2, fields1[i]);
+ }
+ }
+
+ util::MessageDifferencer differencer;
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2,
+ compare_fields, compare_fields));
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsDetectsDifferencesTest) {
+ // Change all of the repeated fields in one of the messages, and use only
+ // those fields for comparison.
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+ TestUtil::ModifyRepeatedFields(&msg2);
+
+ vector<const FieldDescriptor*> fields1;
+ msg1.GetReflection()->ListFields(msg1, &fields1);
+
+ vector<const FieldDescriptor*> compare_fields;
+ // Only compare the repeated field descriptors.
+ for (int i = 0; i < fields1.size(); ++i) {
+ if (fields1[i]->is_repeated()) {
+ compare_fields.push_back(fields1[i]);
+ }
+ }
+
+ util::MessageDifferencer differencer;
+ EXPECT_FALSE(differencer.CompareWithFields(msg1, msg2,
+ compare_fields, compare_fields));
+}
+
+TEST(MessageDifferencerTest, SpecifiedFieldsEquivalenceAllTest) {
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+
+ TestUtil::SetAllFields(&msg1);
+ TestUtil::SetAllFields(&msg2);
+
+ vector<const FieldDescriptor*> fields1;
+ vector<const FieldDescriptor*> fields2;
+ msg1.GetReflection()->ListFields(msg1, &fields1);
+ msg2.GetReflection()->ListFields(msg2, &fields2);
+
+ util::MessageDifferencer differencer;
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2, fields1, fields2));
+}
+
+TEST(MessageDifferencerTest,
+ SpecifiedFieldsEquivalenceIgnoresOtherFieldDifferencesTest) {
+ unittest::TestAllTypes msg1;
+ unittest::TestAllTypes msg2;
+ const Descriptor* desc = msg1.GetDescriptor();
+
+ const FieldDescriptor* optional_int32_desc =
+ desc->FindFieldByName("optional_int32");
+ const FieldDescriptor* optional_int64_desc =
+ desc->FindFieldByName("optional_int64");
+ const FieldDescriptor* default_int64_desc =
+ desc->FindFieldByName("default_int64");
+ ASSERT_TRUE(optional_int32_desc != NULL);
+ ASSERT_TRUE(optional_int64_desc != NULL);
+ ASSERT_TRUE(default_int64_desc != NULL);
+ msg1.set_optional_int32(0);
+ msg2.set_optional_int64(0);
+ msg1.set_default_int64(default_int64_desc->default_value_int64());
+
+ // Set a field to a non-default value so we know that field selection is
+ // actually doing something.
+ msg2.set_optional_uint64(23);
+
+ vector<const FieldDescriptor*> fields1;
+ vector<const FieldDescriptor*> fields2;
+ fields1.push_back(optional_int32_desc);
+ fields1.push_back(default_int64_desc);
+
+ fields2.push_back(optional_int64_desc);
+
+ util::MessageDifferencer differencer;
+ EXPECT_FALSE(differencer.CompareWithFields(msg1, msg2, fields1, fields2));
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2, fields1, fields2));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldSetTest_SetOfSet) {
+ // Create the testing protos
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->add_ra(1); item->add_ra(2); item->add_ra(3);
+ item = msg1.add_item();
+ item->add_ra(5); item->add_ra(6);
+ item = msg1.add_item();
+ item->add_ra(1); item->add_ra(3);
+ item = msg1.add_item();
+ item->add_ra(6); item->add_ra(7); item->add_ra(8);
+
+ item = msg2.add_item();
+ item->add_ra(6); item->add_ra(5);
+ item = msg2.add_item();
+ item->add_ra(6); item->add_ra(8); item->add_ra(7);
+ item = msg2.add_item();
+ item->add_ra(1); item->add_ra(3);
+ item = msg2.add_item();
+ item->add_ra(3); item->add_ra(2); item->add_ra(1);
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldSetTest_Combination) {
+ // Create the testing protos
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ // Treat "item" as Map, with key = "a"
+ // Treat "item.ra" also as Set
+ // Treat "rv" as Set
+ // Treat "rw" as List
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->set_a(3);
+ item->add_ra(1); item->add_ra(2); item->add_ra(3);
+ item = msg1.add_item();
+ item->set_a(4);
+ item->add_ra(5); item->add_ra(6);
+ item = msg1.add_item();
+ item->set_a(1);
+ item->add_ra(1); item->add_ra(3);
+ item = msg1.add_item();
+ item->set_a(2);
+ item->add_ra(6); item->add_ra(7); item->add_ra(8);
+
+ item = msg2.add_item();
+ item->set_a(4);
+ item->add_ra(6); item->add_ra(5);
+ item = msg2.add_item();
+ item->set_a(2);
+ item->add_ra(6); item->add_ra(8); item->add_ra(7);
+ item = msg2.add_item();
+ item->set_a(1);
+ item->add_ra(1); item->add_ra(3);
+ item = msg2.add_item();
+ item->set_a(3);
+ item->add_ra(3); item->add_ra(2); item->add_ra(1);
+
+ msg1.add_rv(3);
+ msg1.add_rv(4);
+ msg1.add_rv(7);
+ msg1.add_rv(0);
+ msg2.add_rv(4);
+ msg2.add_rv(3);
+ msg2.add_rv(0);
+ msg2.add_rv(7);
+
+ msg1.add_rw("nothing"); msg2.add_rw("nothing");
+ msg1.add_rw("should"); msg2.add_rw("should");
+ msg1.add_rw("change"); msg2.add_rw("change");
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.TreatAsMap(msg1.GetDescriptor()->FindFieldByName("item"),
+ item->GetDescriptor()->FindFieldByName("a"));
+ differencer.TreatAsSet(msg1.GetDescriptor()->FindFieldByName("rv"));
+ differencer.TreatAsSet(item->GetDescriptor()->FindFieldByName("ra"));
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldMapTest_Partial) {
+ protobuf_unittest::TestDiffMessage msg1;
+ // message msg1 {
+ // item { a: 1; b: "11" }
+ // }
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->set_a(1);
+ item->set_b("11");
+
+ protobuf_unittest::TestDiffMessage msg2;
+ // message msg2 {
+ // item { a: 2; b: "22" }
+ // item { a: 1; b: "11" }
+ // }
+ item = msg2.add_item();
+ item->set_a(2);
+ item->set_b("22");
+ item = msg2.add_item();
+ item->set_a(1);
+ item->set_b("11");
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.TreatAsMap(GetFieldDescriptor(msg1, "item"),
+ GetFieldDescriptor(msg1, "item.a"));
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldSetTest_Duplicates) {
+ protobuf_unittest::TestDiffMessage a, b, c;
+ // message a: {
+ // rv: 0
+ // rv: 1
+ // rv: 0
+ // }
+ a.add_rv(0);
+ a.add_rv(1);
+ a.add_rv(0);
+ // message b: {
+ // rv: 0
+ // rv: 0
+ // rv: 1
+ // }
+ b.add_rv(0);
+ b.add_rv(0);
+ b.add_rv(1);
+ // message c: {
+ // rv: 0
+ // rv: 1
+ // }
+ c.add_rv(0);
+ c.add_rv(1);
+ util::MessageDifferencer differencer;
+ differencer.TreatAsSet(GetFieldDescriptor(a, "rv"));
+ EXPECT_TRUE(differencer.Compare(b, a));
+ EXPECT_FALSE(differencer.Compare(c, a));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldSetTest_PartialSimple) {
+ protobuf_unittest::TestDiffMessage a, b, c;
+ // message a: {
+ // rm { c: 1 }
+ // rm { c: 0 }
+ // }
+ a.add_rm()->set_c(1);
+ a.add_rm()->set_c(0);
+ // message b: {
+ // rm { c: 1 }
+ // rm {}
+ // }
+ b.add_rm()->set_c(1);
+ b.add_rm();
+ // message c: {
+ // rm {}
+ // rm { c: 1 }
+ // }
+ c.add_rm();
+ c.add_rm()->set_c(1);
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ differencer.TreatAsSet(GetFieldDescriptor(a, "rm"));
+ EXPECT_TRUE(differencer.Compare(b, a));
+ EXPECT_TRUE(differencer.Compare(c, a));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldSetTest_Partial) {
+ protobuf_unittest::TestDiffMessage msg1, msg2;
+ // message msg1: {
+ // rm { a: 1 }
+ // rm { b: 2 }
+ // rm { c: 3 }
+ // }
+ msg1.add_rm()->set_a(1);
+ msg1.add_rm()->set_b(2);
+ msg1.add_rm()->set_c(3);
+ // message msg2: {
+ // rm { a: 1; c: 3 }
+ // rm { b: 2; c: 3 }
+ // rm { b: 2 }
+ // }
+ protobuf_unittest::TestField* field = msg2.add_rm();
+ field->set_a(1);
+ field->set_c(3);
+ field = msg2.add_rm();
+ field->set_b(2);
+ field->set_c(3);
+ field = msg2.add_rm();
+ field->set_b(2);
+
+ util::MessageDifferencer differencer;
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ differencer.TreatAsSet(GetFieldDescriptor(msg1, "rm"));
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldMapTest_MultipleFieldsAsKey) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ // Treat "item" as Map, with key = ("a", "ra")
+ // Treat "item.ra" as Set
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ // key => value: (1, {2, 3}) => "a"
+ item->set_a(1);
+ item->add_ra(2);
+ item->add_ra(3);
+ item->set_b("a");
+ item = msg1.add_item();
+ // key => value: (2, {1, 3}) => "b"
+ item->set_a(2);
+ item->add_ra(1);
+ item->add_ra(3);
+ item->set_b("b");
+ item = msg1.add_item();
+ // key => value: (1, {1, 3}) => "c"
+ item->set_a(1);
+ item->add_ra(1);
+ item->add_ra(3);
+ item->set_b("c");
+
+ item = msg2.add_item();
+ // key => value: (1, {1, 3}) => "c"
+ item->set_a(1);
+ item->add_ra(3);
+ item->add_ra(1);
+ item->set_b("c");
+ item = msg2.add_item();
+ // key => value: (1, {2, 3}) => "a"
+ item->set_a(1);
+ item->add_ra(3);
+ item->add_ra(2);
+ item->set_b("a");
+ item = msg2.add_item();
+ // key => value: (2, {1, 3}) => "b"
+ item->set_a(2);
+ item->add_ra(3);
+ item->add_ra(1);
+ item->set_b("b");
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.TreatAsSet(GetFieldDescriptor(msg1, "item.ra"));
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ vector<const FieldDescriptor*> key_fields;
+ key_fields.push_back(GetFieldDescriptor(msg1, "item.a"));
+ key_fields.push_back(GetFieldDescriptor(msg1, "item.ra"));
+ differencer.TreatAsMapWithMultipleFieldsAsKey(
+ GetFieldDescriptor(msg1, "item"), key_fields);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Introduce some differences.
+ msg1.clear_item();
+ msg2.clear_item();
+ item = msg1.add_item();
+ item->set_a(4);
+ item->add_ra(5);
+ item->add_ra(6);
+ item->set_b("hello");
+ item = msg2.add_item();
+ item->set_a(4);
+ item->add_ra(6);
+ item->add_ra(5);
+ item->set_b("world");
+ string output;
+ differencer.ReportDifferencesToString(&output);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "moved: item[0].ra[0] -> item[0].ra[1] : 5\n"
+ "moved: item[0].ra[1] -> item[0].ra[0] : 6\n"
+ "modified: item[0].b: \"hello\" -> \"world\"\n",
+ output);
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldMapTest_MultipleFieldPathsAsKey) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ // Treat "item" as Map, with key = ("m.a", "m.rc")
+ // Treat "item.m.rc" as Set
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ // key => value: (1, {2, 3}) => "a"
+ item->mutable_m()->set_a(1);
+ item->mutable_m()->add_rc(2);
+ item->mutable_m()->add_rc(3);
+ item->set_b("a");
+ item = msg1.add_item();
+ // key => value: (2, {1, 3}) => "b"
+ item->mutable_m()->set_a(2);
+ item->mutable_m()->add_rc(1);
+ item->mutable_m()->add_rc(3);
+ item->set_b("b");
+ item = msg1.add_item();
+ // key => value: (1, {1, 3}) => "c"
+ item->mutable_m()->set_a(1);
+ item->mutable_m()->add_rc(1);
+ item->mutable_m()->add_rc(3);
+ item->set_b("c");
+
+ item = msg2.add_item();
+ // key => value: (1, {1, 3}) => "c"
+ item->mutable_m()->set_a(1);
+ item->mutable_m()->add_rc(3);
+ item->mutable_m()->add_rc(1);
+ item->set_b("c");
+ item = msg2.add_item();
+ // key => value: (1, {2, 3}) => "a"
+ item->mutable_m()->set_a(1);
+ item->mutable_m()->add_rc(3);
+ item->mutable_m()->add_rc(2);
+ item->set_b("a");
+ item = msg2.add_item();
+ // key => value: (2, {1, 3}) => "b"
+ item->mutable_m()->set_a(2);
+ item->mutable_m()->add_rc(3);
+ item->mutable_m()->add_rc(1);
+ item->set_b("b");
+
+ // Compare
+ util::MessageDifferencer differencer;
+ differencer.TreatAsSet(GetFieldDescriptor(msg1, "item.m.rc"));
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ vector<vector<const FieldDescriptor*> > key_field_paths;
+ vector<const FieldDescriptor*> key_field_path1;
+ key_field_path1.push_back(GetFieldDescriptor(msg1, "item.m"));
+ key_field_path1.push_back(GetFieldDescriptor(msg1, "item.m.a"));
+ vector<const FieldDescriptor*> key_field_path2;
+ key_field_path2.push_back(GetFieldDescriptor(msg1, "item.m"));
+ key_field_path2.push_back(GetFieldDescriptor(msg1, "item.m.rc"));
+ key_field_paths.push_back(key_field_path1);
+ key_field_paths.push_back(key_field_path2);
+ differencer.TreatAsMapWithMultipleFieldPathsAsKey(
+ GetFieldDescriptor(msg1, "item"), key_field_paths);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+
+ // Introduce some differences.
+ msg1.clear_item();
+ msg2.clear_item();
+ item = msg1.add_item();
+ item->mutable_m()->set_a(4);
+ item->mutable_m()->add_rc(5);
+ item->mutable_m()->add_rc(6);
+ item->set_b("hello");
+ item = msg2.add_item();
+ item->mutable_m()->set_a(4);
+ item->mutable_m()->add_rc(6);
+ item->mutable_m()->add_rc(5);
+ item->set_b("world");
+ string output;
+ differencer.ReportDifferencesToString(&output);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "modified: item[0].b: \"hello\" -> \"world\"\n"
+ "moved: item[0].m.rc[0] -> item[0].m.rc[1] : 5\n"
+ "moved: item[0].m.rc[1] -> item[0].m.rc[0] : 6\n",
+ output);
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldMapTest_IgnoredKeyFields) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ // Treat "item" as Map, with key = ("a", "ra")
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->set_a(1);
+ item->add_ra(2);
+ item->set_b("hello");
+ item = msg2.add_item();
+ item->set_a(1);
+ item->add_ra(3);
+ item->set_b("world");
+ // Compare
+ util::MessageDifferencer differencer;
+ vector<const FieldDescriptor*> key_fields;
+ key_fields.push_back(GetFieldDescriptor(msg1, "item.a"));
+ key_fields.push_back(GetFieldDescriptor(msg1, "item.ra"));
+ differencer.TreatAsMapWithMultipleFieldsAsKey(
+ GetFieldDescriptor(msg1, "item"), key_fields);
+ string output;
+ differencer.ReportDifferencesToString(&output);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "added: item[0]: { a: 1 ra: 3 b: \"world\" }\n"
+ "deleted: item[0]: { a: 1 ra: 2 b: \"hello\" }\n",
+ output);
+ // Ignored fields that are listed as parts of the key are still used
+ // in key comparison, but they're not used in value comparison.
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "item.ra"));
+ output.clear();
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "added: item[0]: { a: 1 ra: 3 b: \"world\" }\n"
+ "deleted: item[0]: { a: 1 ra: 2 b: \"hello\" }\n",
+ output);
+ // Ignoring a field in the key is different from treating the left fields
+ // as key. That is:
+ // (key = ("a", "ra") && ignore "ra") != (key = ("a") && ignore "ra")
+ util::MessageDifferencer differencer2;
+ differencer2.TreatAsMap(GetFieldDescriptor(msg1, "item"),
+ GetFieldDescriptor(msg1, "item.a"));
+ differencer2.IgnoreField(GetFieldDescriptor(msg1, "item.ra"));
+ output.clear();
+ differencer2.ReportDifferencesToString(&output);
+ EXPECT_FALSE(differencer2.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "ignored: item[0].ra\n"
+ "modified: item[0].b: \"hello\" -> \"world\"\n",
+ output);
+}
+
+static const char* const kIgnoredFields[] = {"rm.b", "rm.m.b"};
+
+class TestIgnorer : public util::MessageDifferencer::IgnoreCriteria {
+ public:
+ bool IsIgnored(
+ const Message& message1, const Message& message2,
+ const FieldDescriptor* field,
+ const vector<util::MessageDifferencer::SpecificField>& parent_fields)
+ override {
+ string name = "";
+ for (int i = 0; i < parent_fields.size(); ++i) {
+ name += parent_fields[i].field->name() + ".";
+ }
+ name += field->name();
+ for (int i = 0; i < GOOGLE_ARRAYSIZE(kIgnoredFields); ++i) {
+ if (name.compare(kIgnoredFields[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
+TEST(MessageDifferencerTest, TreatRepeatedFieldAsSetWithIgnoredFields) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ TextFormat::MergeFromString("rm { a: 11\n b: 12 }", &msg1);
+ TextFormat::MergeFromString("rm { a: 11\n b: 13 }", &msg2);
+ util::MessageDifferencer differ;
+ differ.TreatAsSet(GetFieldDescriptor(msg1, "rm"));
+ differ.AddIgnoreCriteria(new TestIgnorer);
+ EXPECT_TRUE(differ.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, TreatRepeatedFieldAsMapWithIgnoredKeyFields) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ TextFormat::MergeFromString("rm { a: 11\n m { a: 12\n b: 13\n } }", &msg1);
+ TextFormat::MergeFromString("rm { a: 11\n m { a: 12\n b: 14\n } }", &msg2);
+ util::MessageDifferencer differ;
+ differ.TreatAsMap(GetFieldDescriptor(msg1, "rm"),
+ GetFieldDescriptor(msg1, "rm.m"));
+ differ.AddIgnoreCriteria(new TestIgnorer);
+ EXPECT_TRUE(differ.Compare(msg1, msg2));
+}
+
+// Takes the product of all elements of item.ra as the key for key comparison.
+class ValueProductMapKeyComparator
+ : public util::MessageDifferencer::MapKeyComparator {
+ public:
+ virtual bool IsMatch(const Message &message1,
+ const Message &message2) const {
+ const Reflection* reflection1 = message1.GetReflection();
+ const Reflection* reflection2 = message2.GetReflection();
+ // FieldDescriptor for item.ra
+ const FieldDescriptor* ra_field =
+ message1.GetDescriptor()->FindFieldByName("ra");
+ // Get the product of all elements in item.ra
+ int result1 = 1, result2 = 1;
+ for (int i = 0; i < reflection1->FieldSize(message1, ra_field); ++i) {
+ result1 *= reflection1->GetRepeatedInt32(message1, ra_field, i);
+ }
+ for (int i = 0; i < reflection2->FieldSize(message2, ra_field); ++i) {
+ result2 *= reflection2->GetRepeatedInt32(message2, ra_field, i);
+ }
+ return result1 == result2;
+ }
+};
+
+TEST(MessageDifferencerTest, RepeatedFieldMapTest_CustomMapKeyComparator) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ // Treat "item" as Map, using custom key comparator to determine if two
+ // elements have the same key.
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->add_ra(6);
+ item->add_ra(35);
+ item->set_b("hello");
+ item = msg2.add_item();
+ item->add_ra(10);
+ item->add_ra(21);
+ item->set_b("hello");
+ util::MessageDifferencer differencer;
+ ValueProductMapKeyComparator key_comparator;
+ differencer.TreatAsMapUsingKeyComparator(
+ GetFieldDescriptor(msg1, "item"), &key_comparator);
+ string output;
+ differencer.ReportDifferencesToString(&output);
+ // Though the above two messages have different values for item.ra, they
+ // are regarded as having the same key because 6 * 35 == 10 * 21. That's
+ // how the key comparator determines if the two have the same key.
+ // However, in value comparison, all fields of the message are taken into
+ // consideration, so they are different because their item.ra fields have
+ // different values using normal value comparison.
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "modified: item[0].ra[0]: 6 -> 10\n"
+ "modified: item[0].ra[1]: 35 -> 21\n",
+ output);
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "item.ra"));
+ output.clear();
+ // item.ra is ignored in value comparison, so the two messages equal.
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ("ignored: item[0].ra\n", output);
+}
+
+TEST(MessageDifferencerTest, RepeatedFieldSetTest_Subset) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ msg1.add_rv(3);
+ msg1.add_rv(8);
+ msg1.add_rv(2);
+ msg2.add_rv(2);
+ msg2.add_rv(3);
+ msg2.add_rv(5);
+ msg2.add_rv(8);
+
+ util::MessageDifferencer differencer;
+
+ // Fail with only partial scope set.
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ differencer.set_repeated_field_comparison(util::MessageDifferencer::AS_LIST);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Fail with only set-like comparison set.
+ differencer.set_scope(util::MessageDifferencer::FULL);
+ differencer.set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ // Succeed with scope and repeated field comparison set properly.
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ differencer.set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, IgnoreField_Single) {
+ protobuf_unittest::TestField msg1;
+ protobuf_unittest::TestField msg2;
+
+ msg1.set_c(3);
+ msg1.add_rc(1);
+
+ msg2.set_c(5);
+ msg2.add_rc(1);
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "c"));
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_Repeated) {
+ protobuf_unittest::TestField msg1;
+ protobuf_unittest::TestField msg2;
+
+ msg1.set_c(3);
+ msg1.add_rc(1);
+ msg1.add_rc(2);
+
+ msg2.set_c(3);
+ msg2.add_rc(1);
+ msg2.add_rc(3);
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "rc"));
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_Message) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestField* field;
+
+ field = msg1.add_rm();
+ field->set_c(3);
+
+ field = msg2.add_rm();
+ field->set_c(4);
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "rm"));
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_Group) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestDiffMessage::Item* item;
+
+ item = msg1.add_item();
+ item->set_a(3);
+
+ item = msg2.add_item();
+ item->set_a(4);
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "item"));
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_Missing) {
+ protobuf_unittest::TestField msg1;
+ protobuf_unittest::TestField msg2;
+
+ msg1.set_c(3);
+ msg1.add_rc(1);
+
+ msg2.add_rc(1);
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "c"));
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+ ExpectEqualsWithDifferencer(&differencer, msg2, msg1);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_Multiple) {
+ protobuf_unittest::TestField msg1;
+ protobuf_unittest::TestField msg2;
+
+ msg1.set_c(3);
+ msg1.add_rc(1);
+ msg1.add_rc(2);
+
+ msg2.set_c(5);
+ msg2.add_rc(1);
+ msg2.add_rc(3);
+
+ const FieldDescriptor* c = GetFieldDescriptor(msg1, "c");
+ const FieldDescriptor* rc = GetFieldDescriptor(msg1, "rc");
+
+ { // Ignore c
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(c);
+
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ }
+ { // Ignore rc
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(rc);
+
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+ }
+ { // Ignore both
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(c);
+ differencer.IgnoreField(rc);
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+ }
+}
+
+TEST(MessageDifferencerTest, IgnoreField_NestedMessage) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestField* field;
+
+ field = msg1.add_rm();
+ field->set_c(3);
+ field->add_rc(1);
+
+ field = msg2.add_rm();
+ field->set_c(4);
+ field->add_rc(1);
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "rm.c"));
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_NestedGroup) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestDiffMessage::Item* item;
+
+ item = msg1.add_item();
+ item->set_a(3);
+ item->set_b("foo");
+
+ item = msg2.add_item();
+ item->set_a(4);
+ item->set_b("foo");
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(GetFieldDescriptor(msg1, "item.a"));
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_InsideSet) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestDiffMessage::Item* item;
+
+ item = msg1.add_item();
+ item->set_a(1);
+ item->set_b("foo");
+ item->add_ra(1);
+
+ item = msg1.add_item();
+ item->set_a(2);
+ item->set_b("bar");
+ item->add_ra(2);
+
+ item = msg2.add_item();
+ item->set_a(2);
+ item->set_b("bar");
+ item->add_ra(2);
+
+ item = msg2.add_item();
+ item->set_a(1);
+ item->set_b("baz");
+ item->add_ra(1);
+
+ const FieldDescriptor* item_desc = GetFieldDescriptor(msg1, "item");
+ const FieldDescriptor* b = GetFieldDescriptor(msg1, "item.b");
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(b);
+ differencer.TreatAsSet(item_desc);
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_InsideMap) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestDiffMessage::Item* item;
+
+ item = msg1.add_item();
+ item->set_a(1);
+ item->set_b("foo");
+ item->add_ra(1);
+
+ item = msg1.add_item();
+ item->set_a(2);
+ item->set_b("bar");
+ item->add_ra(2);
+
+ item = msg2.add_item();
+ item->set_a(2);
+ item->set_b("bar");
+ item->add_ra(2);
+
+ item = msg2.add_item();
+ item->set_a(1);
+ item->set_b("baz");
+ item->add_ra(1);
+
+ const FieldDescriptor* item_desc = GetFieldDescriptor(msg1, "item");
+ const FieldDescriptor* a = GetFieldDescriptor(msg1, "item.a");
+ const FieldDescriptor* b = GetFieldDescriptor(msg1, "item.b");
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(b);
+ differencer.TreatAsMap(item_desc, a);
+
+ ExpectEqualsWithDifferencer(&differencer, msg1, msg2);
+}
+
+TEST(MessageDifferencerTest, IgnoreField_DoesNotIgnoreKey) {
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestDiffMessage::Item* item;
+
+ item = msg1.add_item();
+ item->set_a(1);
+ item->set_b("foo");
+ item->add_ra(1);
+
+ item = msg2.add_item();
+ item->set_a(2);
+ item->set_b("foo");
+ item->add_ra(1);
+
+ const FieldDescriptor* item_desc = GetFieldDescriptor(msg1, "item");
+ const FieldDescriptor* a = GetFieldDescriptor(msg1, "item.a");
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(a);
+ differencer.TreatAsMap(item_desc, a);
+
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+}
+
+TEST(MessageDifferencerTest, IgnoreField_TrumpsCompareWithFields) {
+ protobuf_unittest::TestField msg1;
+ protobuf_unittest::TestField msg2;
+
+ msg1.set_c(3);
+ msg1.add_rc(1);
+ msg1.add_rc(2);
+
+ msg2.set_c(3);
+ msg2.add_rc(1);
+ msg2.add_rc(3);
+
+ const FieldDescriptor* c = GetFieldDescriptor(msg1, "c");
+ const FieldDescriptor* rc = GetFieldDescriptor(msg1, "rc");
+
+ vector<const FieldDescriptor*> fields;
+ fields.push_back(c);
+ fields.push_back(rc);
+
+ util::MessageDifferencer differencer;
+ differencer.IgnoreField(rc);
+
+ differencer.set_scope(util::MessageDifferencer::FULL);
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2, fields, fields));
+
+ differencer.set_scope(util::MessageDifferencer::PARTIAL);
+ EXPECT_TRUE(differencer.CompareWithFields(msg1, msg2, fields, fields));
+}
+
+
+// Test class to save a copy of the last field_context.parent_fields() vector
+// passed to the comparison function.
+class ParentSavingFieldComparator : public util::FieldComparator {
+ public:
+ ParentSavingFieldComparator() {}
+
+ virtual ComparisonResult Compare(
+ const google::protobuf::Message& message_1,
+ const google::protobuf::Message& message_2,
+ const google::protobuf::FieldDescriptor* field,
+ int index_1, int index_2,
+ const google::protobuf::util::FieldContext* field_context) {
+ if (field_context)
+ parent_fields_ = *(field_context->parent_fields());
+ if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ return RECURSE;
+ } else {
+ return SAME;
+ }
+ }
+
+ vector<google::protobuf::util::MessageDifferencer::SpecificField> parent_fields() {
+ return parent_fields_;
+ }
+
+ private:
+ vector<google::protobuf::util::MessageDifferencer::SpecificField> parent_fields_;
+};
+
+// Tests if MessageDifferencer sends the parent fields in the FieldContext
+// parameter.
+TEST(MessageDifferencerTest, FieldContextParentFieldsTest) {
+ protobuf_unittest::TestDiffMessage msg1;
+ msg1.add_rm()->set_c(1);
+ protobuf_unittest::TestDiffMessage msg2;
+ msg2.add_rm()->set_c(1);
+
+ ParentSavingFieldComparator field_comparator;
+ util::MessageDifferencer differencer;
+ differencer.set_field_comparator(&field_comparator);
+ differencer.Compare(msg1, msg2);
+
+ // We want only one parent with the name "rm"
+ ASSERT_EQ(1, field_comparator.parent_fields().size());
+ EXPECT_EQ("rm", field_comparator.parent_fields()[0].field->name());
+}
+
+
+class ComparisonTest : public testing::Test {
+ protected:
+ ComparisonTest() : use_equivalency_(false), repeated_field_as_set_(false) {
+ // Setup the test.
+ TestUtil::SetAllFields(&proto1_);
+ TestUtil::SetAllFields(&proto2_);
+
+ TestUtil::SetAllExtensions(&proto1ex_);
+ TestUtil::SetAllExtensions(&proto2ex_);
+
+ TestUtil::SetAllFieldsAndExtensions(&orderings_proto1_);
+ TestUtil::SetAllFieldsAndExtensions(&orderings_proto2_);
+
+ unknown1_ = empty1_.mutable_unknown_fields();
+ unknown2_ = empty2_.mutable_unknown_fields();
+ }
+
+ ~ComparisonTest() { }
+
+ void SetSpecialFieldOption(const Message& message,
+ util::MessageDifferencer* d) {
+ if (!ignored_field_.empty()) {
+ d->IgnoreField(GetFieldDescriptor(message, ignored_field_));
+ }
+
+ if (repeated_field_as_set_) {
+ d->set_repeated_field_comparison(util::MessageDifferencer::AS_SET);
+ }
+
+ if (!set_field_.empty()) {
+ d->TreatAsSet(GetFieldDescriptor(message, set_field_));
+ }
+
+ if (!map_field_.empty() && !map_key_.empty()) {
+ d->TreatAsMap(GetFieldDescriptor(message, map_field_),
+ GetFieldDescriptor(message, map_field_ + "." + map_key_));
+ }
+ }
+
+ string Run(const Message& msg1, const Message& msg2) {
+ string output;
+
+ // Setup the comparison.
+ util::MessageDifferencer differencer;
+ differencer.ReportDifferencesToString(&output);
+
+ if (use_equivalency_) {
+ differencer.set_message_field_comparison(
+ util::MessageDifferencer::EQUIVALENT);
+ }
+
+ SetSpecialFieldOption(msg1, &differencer);
+
+ // Conduct the comparison.
+ EXPECT_FALSE(differencer.Compare(msg1, msg2));
+
+ return output;
+ }
+
+ string Run() {
+ return Run(proto1_, proto2_);
+ }
+
+ string RunOrder() {
+ return Run(orderings_proto1_, orderings_proto2_);
+ }
+
+ string RunEx() {
+ return Run(proto1ex_, proto2ex_);
+ }
+
+ string RunDiff() {
+ return Run(proto1diff_, proto2diff_);
+ }
+
+ string RunUn() {
+ return Run(empty1_, empty2_);
+ }
+
+ void use_equivalency() {
+ use_equivalency_ = true;
+ }
+
+ void repeated_field_as_set() {
+ repeated_field_as_set_ = true;
+ }
+
+ void field_as_set(const string& field) {
+ set_field_ = field;
+ }
+
+ void field_as_map(const string& field, const string& key) {
+ map_field_ = field;
+ map_key_ = key;
+ }
+
+ void ignore_field(const string& field) {
+ ignored_field_ = field;
+ }
+
+ unittest::TestAllTypes proto1_;
+ unittest::TestAllTypes proto2_;
+
+ unittest::TestFieldOrderings orderings_proto1_;
+ unittest::TestFieldOrderings orderings_proto2_;
+
+ unittest::TestAllExtensions proto1ex_;
+ unittest::TestAllExtensions proto2ex_;
+
+ unittest::TestDiffMessage proto1diff_;
+ unittest::TestDiffMessage proto2diff_;
+
+ unittest::TestEmptyMessage empty1_;
+ unittest::TestEmptyMessage empty2_;
+
+ UnknownFieldSet* unknown1_;
+ UnknownFieldSet* unknown2_;
+
+ bool use_equivalency_;
+ bool repeated_field_as_set_;
+
+ string set_field_;
+ string map_field_;
+ string map_key_;
+ string ignored_field_;
+};
+
+// Basic tests.
+TEST_F(ComparisonTest, AdditionTest) {
+ proto1_.clear_optional_int32();
+
+ EXPECT_EQ("added: optional_int32: 101\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, Addition_OrderTest) {
+ orderings_proto1_.clear_my_int();
+
+ EXPECT_EQ("added: my_int: 1\n",
+ RunOrder());
+}
+
+TEST_F(ComparisonTest, DeletionTest) {
+ proto2_.clear_optional_int32();
+
+ EXPECT_EQ("deleted: optional_int32: 101\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, Deletion_OrderTest) {
+ orderings_proto2_.clear_my_string();
+
+ EXPECT_EQ("deleted: my_string: \"foo\"\n",
+ RunOrder());
+}
+
+TEST_F(ComparisonTest, RepeatedDeletionTest) {
+ proto2_.clear_repeated_int32();
+
+ EXPECT_EQ("deleted: repeated_int32[0]: 201\n"
+ "deleted: repeated_int32[1]: 301\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, ModificationTest) {
+ proto1_.set_optional_int32(-1);
+
+ EXPECT_EQ("modified: optional_int32: -1 -> 101\n",
+ Run());
+}
+
+// Basic equivalency tests.
+TEST_F(ComparisonTest, EquivalencyAdditionTest) {
+ use_equivalency();
+
+ proto1_.clear_optional_int32();
+
+ EXPECT_EQ("modified: optional_int32: 0 -> 101\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, EquivalencyDeletionTest) {
+ use_equivalency();
+
+ proto2_.clear_optional_int32();
+
+ EXPECT_EQ("modified: optional_int32: 101 -> 0\n",
+ Run());
+}
+
+// Group tests.
+TEST_F(ComparisonTest, GroupAdditionTest) {
+ proto1_.mutable_optionalgroup()->clear_a();
+
+ EXPECT_EQ("added: optionalgroup.a: 117\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, GroupDeletionTest) {
+ proto2_.mutable_optionalgroup()->clear_a();
+
+ EXPECT_EQ("deleted: optionalgroup.a: 117\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, GroupModificationTest) {
+ proto1_.mutable_optionalgroup()->set_a(2);
+
+ EXPECT_EQ("modified: optionalgroup.a: 2 -> 117\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, GroupFullAdditionTest) {
+ proto1_.clear_optionalgroup();
+
+ // Note the difference in the output between this and GroupAdditionTest.
+ EXPECT_EQ("added: optionalgroup: { a: 117 }\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, GroupFullDeletionTest) {
+ proto2_.clear_optionalgroup();
+
+ EXPECT_EQ("deleted: optionalgroup: { a: 117 }\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, RepeatedSetOptionTest) {
+ repeated_field_as_set();
+
+ proto2_.clear_repeatedgroup();
+ proto1_.clear_repeatedgroup();
+ proto1_.add_repeatedgroup()->set_a(317);
+ proto2_.add_repeatedgroup()->set_a(909);
+ proto2_.add_repeatedgroup()->set_a(907);
+ proto1_.add_repeatedgroup()->set_a(904);
+ proto1_.add_repeatedgroup()->set_a(907);
+ proto1_.add_repeatedgroup()->set_a(909);
+
+ EXPECT_EQ("moved: repeatedgroup[2] -> repeatedgroup[1] : { a: 907 }\n"
+ "moved: repeatedgroup[3] -> repeatedgroup[0] : { a: 909 }\n"
+ "deleted: repeatedgroup[0]: { a: 317 }\n"
+ "deleted: repeatedgroup[1]: { a: 904 }\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, RepeatedSetOptionTest_Ex) {
+ repeated_field_as_set();
+
+ proto1ex_.ClearExtension(protobuf_unittest::repeated_nested_message_extension);
+ proto2ex_.ClearExtension(protobuf_unittest::repeated_nested_message_extension);
+ proto2ex_.AddExtension(protobuf_unittest::repeated_nested_message_extension)
+ ->set_bb(909);
+ proto2ex_.AddExtension(protobuf_unittest::repeated_nested_message_extension)
+ ->set_bb(907);
+ proto1ex_.AddExtension(protobuf_unittest::repeated_nested_message_extension)
+ ->set_bb(904);
+ proto1ex_.AddExtension(protobuf_unittest::repeated_nested_message_extension)
+ ->set_bb(907);
+ proto1ex_.AddExtension(protobuf_unittest::repeated_nested_message_extension)
+ ->set_bb(909);
+
+ EXPECT_EQ("moved: (protobuf_unittest.repeated_nested_message_extension)[2] ->"
+ " (protobuf_unittest.repeated_nested_message_extension)[0] :"
+ " { bb: 909 }\n"
+ "deleted: (protobuf_unittest.repeated_nested_message_extension)[0]:"
+ " { bb: 904 }\n",
+ RunEx());
+}
+
+TEST_F(ComparisonTest, RepeatedMapFieldTest_Group) {
+ field_as_map("repeatedgroup", "a");
+ proto1_.clear_repeatedgroup();
+ proto2_.clear_repeatedgroup();
+
+ proto1_.add_repeatedgroup()->set_a(317); // deleted
+ proto1_.add_repeatedgroup()->set_a(904); // deleted
+ proto1_.add_repeatedgroup()->set_a(907); // moved from
+ proto1_.add_repeatedgroup()->set_a(909); // moved from
+
+ proto2_.add_repeatedgroup()->set_a(909); // moved to
+ proto2_.add_repeatedgroup()->set_a(318); // added
+ proto2_.add_repeatedgroup()->set_a(907); // moved to
+
+ EXPECT_EQ("moved: repeatedgroup[3] -> repeatedgroup[0] : { a: 909 }\n"
+ "added: repeatedgroup[1]: { a: 318 }\n"
+ "deleted: repeatedgroup[0]: { a: 317 }\n"
+ "deleted: repeatedgroup[1]: { a: 904 }\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, RepeatedMapFieldTest_MessageKey) {
+ // Use m as key, but use b as value.
+ field_as_map("item", "m");
+
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+
+ // The following code creates one deletion, one addition and two moved fields
+ // on the messages.
+ item->mutable_m()->set_c(0);
+ item->set_b("first");
+ item = msg1.add_item();
+ item->mutable_m()->set_c(2);
+ item->set_b("second");
+ item = msg1.add_item(); item->set_b("null"); // empty key moved
+ item = msg1.add_item();
+ item->mutable_m()->set_c(3);
+ item->set_b("third"); // deletion
+ item = msg1.add_item();
+ item->mutable_m()->set_c(2);
+ item->set_b("second"); // duplicated key ( deletion )
+ item = msg2.add_item();
+ item->mutable_m()->set_c(2);
+ item->set_b("second"); // modification
+ item = msg2.add_item();
+ item->mutable_m()->set_c(4);
+ item->set_b("fourth"); // addition
+ item = msg2.add_item();
+ item->mutable_m()->set_c(0);
+ item->set_b("fist"); // move with change
+ item = msg2.add_item(); item->set_b("null");
+
+ EXPECT_EQ(
+ "modified: item[0].b -> item[2].b: \"first\" -> \"fist\"\n"
+ "moved: item[1] -> item[0] : { b: \"second\" m { c: 2 } }\n"
+ "moved: item[2] -> item[3] : { b: \"null\" }\n"
+ "added: item[1]: { b: \"fourth\" m { c: 4 } }\n"
+ "deleted: item[3]: { b: \"third\" m { c: 3 } }\n"
+ "deleted: item[4]: { b: \"second\" m { c: 2 } }\n",
+ Run(msg1, msg2));
+}
+
+TEST_F(ComparisonTest, RepeatedFieldSetTest_SetOfSet) {
+ repeated_field_as_set();
+ // Create the testing protos
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->add_ra(1); item->add_ra(2); item->add_ra(3);
+ item = msg1.add_item();
+ item->add_ra(5); item->add_ra(6);
+ item = msg1.add_item();
+ item->add_ra(1); item->add_ra(3);
+ item = msg1.add_item();
+ item->add_ra(6); item->add_ra(7); item->add_ra(8);
+
+ item = msg2.add_item();
+ item->add_ra(6); item->add_ra(5);
+ item = msg2.add_item();
+ item->add_ra(6); item->add_ra(8);
+ item = msg2.add_item();
+ item->add_ra(1); item->add_ra(3);
+ item = msg2.add_item();
+ item->add_ra(3); item->add_ra(2); item->add_ra(1);
+
+ // Compare
+ EXPECT_EQ("moved: item[0].ra[0] -> item[3].ra[2] : 1\n"
+ "moved: item[0].ra[2] -> item[3].ra[0] : 3\n"
+ "moved: item[0] -> item[3] : { ra: 1 ra: 2 ra: 3 }\n"
+ "moved: item[1].ra[0] -> item[0].ra[1] : 5\n"
+ "moved: item[1].ra[1] -> item[0].ra[0] : 6\n"
+ "moved: item[1] -> item[0] : { ra: 5 ra: 6 }\n"
+ "added: item[1]: { ra: 6 ra: 8 }\n"
+ "deleted: item[3]: { ra: 6 ra: 7 ra: 8 }\n",
+ Run(msg1, msg2));
+}
+
+TEST_F(ComparisonTest, RepeatedMapFieldTest_RepeatedKey) {
+ // used rb as a key, but b is the value.
+ repeated_field_as_set();
+ field_as_map("item", "rb");
+
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->add_rb("a");
+ item->add_rb("b");
+ item->set_b("first");
+
+ item = msg2.add_item();
+ item->add_rb("c");
+ item->set_b("second");
+
+ item = msg2.add_item();
+ item->add_rb("b");
+ item->add_rb("a");
+ item->set_b("fist");
+
+
+ EXPECT_EQ("modified: item[0].b -> item[1].b: \"first\" -> \"fist\"\n"
+ "moved: item[0].rb[0] -> item[1].rb[1] : \"a\"\n"
+ "moved: item[0].rb[1] -> item[1].rb[0] : \"b\"\n"
+ "added: item[0]: { b: \"second\" rb: \"c\" }\n",
+ Run(msg1, msg2));
+}
+
+TEST_F(ComparisonTest, RepeatedMapFieldTest_RepeatedMessageKey) {
+ field_as_map("item", "rm");
+
+ protobuf_unittest::TestDiffMessage msg1;
+ protobuf_unittest::TestDiffMessage msg2;
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ protobuf_unittest::TestField* key = item->add_rm();
+ key->set_c(2); key->add_rc(10); key->add_rc(10);
+ item = msg1.add_item(); key = item->add_rm();
+ key->set_c(0); key->add_rc(1); key->add_rc(2);
+ key = item->add_rm();
+ key->set_c(0);
+ item->add_rb("first");
+
+ item = msg2.add_item();
+ item->CopyFrom(msg1.item(1));
+ item->add_rb("second");
+
+ EXPECT_EQ("added: item[0].rb[1]: \"second\"\n"
+ "deleted: item[0]: { rm { c: 2 rc: 10 rc: 10 } }\n",
+ Run(msg1, msg2));
+}
+
+TEST_F(ComparisonTest, RepeatedSetOptionTest_Unknown) {
+ // Currently, as_set option doens't have affects on unknown field.
+ // If needed, this feature will be added by request.
+ repeated_field_as_set();
+ unknown1_->AddGroup(245)->AddFixed32(248, 1);
+ unknown2_->AddGroup(245)->AddFixed32(248, 3);
+ unknown2_->AddGroup(245)->AddFixed32(248, 1);
+
+ // We expect it behaves the same as normal comparison.
+ EXPECT_EQ("modified: 245[0].248[0]: 0x00000001 -> 0x00000003\n"
+ "added: 245[1]: { ... }\n",
+ RunUn());
+}
+
+TEST_F(ComparisonTest, Matching_Unknown) {
+ unknown1_->AddGroup(245)->AddFixed32(248, 1);
+ unknown2_->AddGroup(245)->AddFixed32(248, 1);
+ unknown1_->AddGroup(245)->AddFixed32(248, 3);
+ unknown2_->AddGroup(245)->AddFixed32(248, 3);
+ unknown2_->AddLengthDelimited(242, "cat");
+ unknown2_->AddGroup(246)->AddFixed32(248, 4);
+
+ // report_match is false so only added/modified fields are expected.
+ EXPECT_EQ("added: 242[0]: \"cat\"\n"
+ "added: 246[0]: { ... }\n",
+ RunUn());
+}
+
+TEST_F(ComparisonTest, RepeatedSetFieldTest) {
+ field_as_set("repeatedgroup");
+
+ proto1_.clear_repeatedgroup();
+ proto2_.clear_repeatedgroup();
+ proto2_.add_repeatedgroup()->set_a(909);
+ proto2_.add_repeatedgroup()->set_a(907);
+ proto1_.add_repeatedgroup()->set_a(317);
+ proto1_.add_repeatedgroup()->set_a(904);
+ proto1_.add_repeatedgroup()->set_a(907);
+ proto1_.add_repeatedgroup()->set_a(909);
+
+ EXPECT_EQ("moved: repeatedgroup[2] -> repeatedgroup[1] : { a: 907 }\n"
+ "moved: repeatedgroup[3] -> repeatedgroup[0] : { a: 909 }\n"
+ "deleted: repeatedgroup[0]: { a: 317 }\n"
+ "deleted: repeatedgroup[1]: { a: 904 }\n",
+ Run());
+}
+
+// Embedded message tests.
+TEST_F(ComparisonTest, EmbeddedAdditionTest) {
+ proto1_.mutable_optional_nested_message()->clear_bb();
+
+ EXPECT_EQ("added: optional_nested_message.bb: 118\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, EmbeddedDeletionTest) {
+ proto2_.mutable_optional_nested_message()->clear_bb();
+
+ EXPECT_EQ("deleted: optional_nested_message.bb: 118\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, EmbeddedModificationTest) {
+ proto1_.mutable_optional_nested_message()->set_bb(2);
+
+ EXPECT_EQ("modified: optional_nested_message.bb: 2 -> 118\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, EmbeddedFullAdditionTest) {
+ proto1_.clear_optional_nested_message();
+
+ EXPECT_EQ("added: optional_nested_message: { bb: 118 }\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, EmbeddedPartialAdditionTest) {
+ proto1_.clear_optional_nested_message();
+ proto2_.mutable_optional_nested_message()->clear_bb();
+
+ EXPECT_EQ("added: optional_nested_message: { }\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, EmbeddedFullDeletionTest) {
+ proto2_.clear_optional_nested_message();
+
+ EXPECT_EQ("deleted: optional_nested_message: { bb: 118 }\n",
+ Run());
+}
+
+// Repeated element tests.
+TEST_F(ComparisonTest, BasicRepeatedTest) {
+ proto1_.clear_repeated_int32();
+ proto2_.clear_repeated_int32();
+
+ proto1_.add_repeated_int32(500);
+ proto1_.add_repeated_int32(501);
+ proto1_.add_repeated_int32(502);
+ proto1_.add_repeated_int32(503);
+ proto1_.add_repeated_int32(500);
+
+ proto2_.add_repeated_int32(500);
+ proto2_.add_repeated_int32(509);
+ proto2_.add_repeated_int32(502);
+ proto2_.add_repeated_int32(504);
+
+ EXPECT_EQ("modified: repeated_int32[1]: 501 -> 509\n"
+ "modified: repeated_int32[3]: 503 -> 504\n"
+ "deleted: repeated_int32[4]: 500\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, BasicRepeatedTest_SetOption) {
+ repeated_field_as_set();
+ proto1_.clear_repeated_int32();
+ proto2_.clear_repeated_int32();
+
+ proto1_.add_repeated_int32(501);
+ proto1_.add_repeated_int32(502);
+ proto1_.add_repeated_int32(503);
+ proto1_.add_repeated_int32(500);
+ proto1_.add_repeated_int32(500);
+
+ proto2_.add_repeated_int32(500);
+ proto2_.add_repeated_int32(509);
+ proto2_.add_repeated_int32(503);
+ proto2_.add_repeated_int32(502);
+ proto2_.add_repeated_int32(504);
+
+ EXPECT_EQ("moved: repeated_int32[1] -> repeated_int32[3] : 502\n"
+ "moved: repeated_int32[3] -> repeated_int32[0] : 500\n"
+ "added: repeated_int32[1]: 509\n"
+ "added: repeated_int32[4]: 504\n"
+ "deleted: repeated_int32[0]: 501\n"
+ "deleted: repeated_int32[4]: 500\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, BasicRepeatedTest_SetField) {
+ field_as_set("repeated_int32");
+ proto1_.clear_repeated_int32();
+ proto2_.clear_repeated_int32();
+
+ proto1_.add_repeated_int32(501);
+ proto1_.add_repeated_int32(502);
+ proto1_.add_repeated_int32(503);
+ proto1_.add_repeated_int32(500);
+ proto1_.add_repeated_int32(500);
+
+ proto2_.add_repeated_int32(500);
+ proto2_.add_repeated_int32(509);
+ proto2_.add_repeated_int32(503);
+ proto2_.add_repeated_int32(502);
+ proto2_.add_repeated_int32(504);
+
+ EXPECT_EQ("moved: repeated_int32[1] -> repeated_int32[3] : 502\n"
+ "moved: repeated_int32[3] -> repeated_int32[0] : 500\n"
+ "added: repeated_int32[1]: 509\n"
+ "added: repeated_int32[4]: 504\n"
+ "deleted: repeated_int32[0]: 501\n"
+ "deleted: repeated_int32[4]: 500\n",
+ Run());
+}
+
+// Multiple action tests.
+TEST_F(ComparisonTest, AddDeleteTest) {
+ proto1_.clear_optional_int32();
+ proto2_.clear_optional_int64();
+
+ EXPECT_EQ("added: optional_int32: 101\n"
+ "deleted: optional_int64: 102\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, AddDelete_FieldOrderingTest) {
+ orderings_proto1_.ClearExtension(unittest::my_extension_string);
+ orderings_proto2_.clear_my_int();
+
+ EXPECT_EQ("deleted: my_int: 1\n"
+ "added: (protobuf_unittest.my_extension_string): \"bar\"\n",
+ RunOrder());
+}
+
+TEST_F(ComparisonTest, AllThreeTest) {
+ proto1_.clear_optional_int32();
+ proto2_.clear_optional_float();
+ proto2_.set_optional_string("hello world!");
+
+ EXPECT_EQ("added: optional_int32: 101\n"
+ "deleted: optional_float: 111\n"
+ "modified: optional_string: \"115\" -> \"hello world!\"\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, SandwhichTest) {
+ proto1_.clear_optional_int64();
+ proto1_.clear_optional_uint32();
+
+ proto2_.clear_optional_uint64();
+
+ EXPECT_EQ("added: optional_int64: 102\n"
+ "added: optional_uint32: 103\n"
+ "deleted: optional_uint64: 104\n",
+ Run());
+}
+
+TEST_F(ComparisonTest, IgnoredNoChangeTest) {
+ proto1diff_.set_v(3);
+ proto2diff_.set_v(3);
+ proto2diff_.set_w("foo");
+
+ ignore_field("v");
+
+ EXPECT_EQ("ignored: v\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredAddTest) {
+ proto2diff_.set_v(3);
+ proto2diff_.set_w("foo");
+
+ ignore_field("v");
+
+ EXPECT_EQ("ignored: v\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredDeleteTest) {
+ proto1diff_.set_v(3);
+ proto2diff_.set_w("foo");
+
+ ignore_field("v");
+
+ EXPECT_EQ("ignored: v\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredModifyTest) {
+ proto1diff_.set_v(3);
+ proto2diff_.set_v(4);
+ proto2diff_.set_w("foo");
+
+ ignore_field("v");
+
+ EXPECT_EQ("ignored: v\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredRepeatedAddTest) {
+ proto1diff_.add_rv(3);
+ proto1diff_.add_rv(4);
+
+ proto2diff_.add_rv(3);
+ proto2diff_.add_rv(4);
+ proto2diff_.add_rv(5);
+
+ proto2diff_.set_w("foo");
+
+ ignore_field("rv");
+
+ EXPECT_EQ("ignored: rv\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredRepeatedDeleteTest) {
+ proto1diff_.add_rv(3);
+ proto1diff_.add_rv(4);
+ proto1diff_.add_rv(5);
+
+ proto2diff_.add_rv(3);
+ proto2diff_.add_rv(4);
+
+ proto2diff_.set_w("foo");
+
+ ignore_field("rv");
+
+ EXPECT_EQ("ignored: rv\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredRepeatedModifyTest) {
+ proto1diff_.add_rv(3);
+ proto1diff_.add_rv(4);
+
+ proto2diff_.add_rv(3);
+ proto2diff_.add_rv(5);
+
+ proto2diff_.set_w("foo");
+
+ ignore_field("rv");
+
+ EXPECT_EQ("ignored: rv\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredWholeNestedMessage) {
+ proto1diff_.mutable_m()->set_c(3);
+ proto2diff_.mutable_m()->set_c(4);
+
+ proto2diff_.set_w("foo");
+
+ ignore_field("m");
+
+ EXPECT_EQ("added: w: \"foo\"\n"
+ "ignored: m\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredNestedField) {
+ proto1diff_.mutable_m()->set_c(3);
+ proto2diff_.mutable_m()->set_c(4);
+
+ proto2diff_.set_w("foo");
+
+ ignore_field("m.c");
+
+ EXPECT_EQ("added: w: \"foo\"\n"
+ "ignored: m.c\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredRepeatedNested) {
+ proto1diff_.add_rm()->set_c(0);
+ proto1diff_.add_rm()->set_c(1);
+ proto2diff_.add_rm()->set_c(2);
+ proto2diff_.add_rm()->set_c(3);
+
+ proto2diff_.set_w("foo");
+
+ ignore_field("rm.c");
+
+ EXPECT_EQ("ignored: rm[0].c\n"
+ "ignored: rm[1].c\n"
+ "added: w: \"foo\"\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, IgnoredNestedRepeated) {
+ proto1diff_.mutable_m()->add_rc(23);
+ proto1diff_.mutable_m()->add_rc(24);
+ proto2diff_.mutable_m()->add_rc(25);
+
+ proto2diff_.set_w("foo");
+
+ ignore_field("m.rc");
+
+ EXPECT_EQ("added: w: \"foo\"\n"
+ "ignored: m.rc\n",
+ RunDiff());
+}
+
+TEST_F(ComparisonTest, ExtensionTest) {
+ proto1ex_.SetExtension(unittest::optional_int32_extension, 401);
+ proto2ex_.SetExtension(unittest::optional_int32_extension, 402);
+
+ proto1ex_.ClearExtension(unittest::optional_int64_extension);
+ proto2ex_.SetExtension(unittest::optional_int64_extension, 403);
+
+ EXPECT_EQ(
+ "modified: (protobuf_unittest.optional_int32_extension): 401 -> 402\n"
+ "added: (protobuf_unittest.optional_int64_extension): 403\n",
+ RunEx());
+}
+
+TEST_F(ComparisonTest, MatchedUnknownFieldTagTest) {
+ unknown1_->AddVarint(240, 122);
+ unknown2_->AddVarint(240, 121);
+ unknown1_->AddFixed32(241, 1);
+ unknown2_->AddFixed64(241, 2);
+ unknown1_->AddLengthDelimited(242, "cat");
+ unknown2_->AddLengthDelimited(242, "dog");
+
+ EXPECT_EQ(
+ "modified: 240[0]: 122 -> 121\n"
+ "deleted: 241[0]: 0x00000001\n"
+ "added: 241[0]: 0x0000000000000002\n"
+ "modified: 242[0]: \"cat\" -> \"dog\"\n",
+ RunUn());
+}
+
+TEST_F(ComparisonTest, UnmatchedUnknownFieldTagTest) {
+ unknown1_->AddFixed32(243, 1);
+ unknown2_->AddVarint(244, 2);
+ unknown2_->AddVarint(244, 4);
+
+ EXPECT_EQ(
+ "deleted: 243[0]: 0x00000001\n"
+ "added: 244[0]: 2\n"
+ "added: 244[1]: 4\n",
+ RunUn());
+}
+
+TEST_F(ComparisonTest, DifferentSizedUnknownFieldTest) {
+ unknown1_->AddVarint(240, 1);
+ unknown1_->AddVarint(240, 3);
+ unknown1_->AddVarint(240, 4);
+ unknown2_->AddVarint(240, 2);
+ unknown2_->AddVarint(240, 3);
+ unknown2_->AddVarint(240, 2);
+ unknown2_->AddVarint(240, 5);
+
+ EXPECT_EQ(
+ "modified: 240[0]: 1 -> 2\n"
+ "modified: 240[2]: 4 -> 2\n"
+ "added: 240[3]: 5\n",
+ RunUn());
+}
+
+TEST_F(ComparisonTest, UnknownFieldsAll) {
+ unknown1_->AddVarint(243, 122);
+ unknown1_->AddFixed64(244, 0x0172356);
+ unknown1_->AddFixed64(244, 0x098);
+ unknown1_->AddGroup(245)->AddFixed32(248, 1);
+ unknown1_->mutable_field(3)->mutable_group()->AddFixed32(248, 2);
+ unknown1_->AddGroup(249)->AddFixed64(250, 1);
+
+ unknown2_->AddVarint(243, 121);
+ unknown2_->AddLengthDelimited(73882, "test 123");
+ unknown2_->AddGroup(245)->AddFixed32(248, 3);
+ unknown2_->AddGroup(247);
+
+ EXPECT_EQ(
+ "modified: 243[0]: 122 -> 121\n"
+ "deleted: 244[0]: 0x0000000000172356\n"
+ "deleted: 244[1]: 0x0000000000000098\n"
+ "modified: 245[0].248[0]: 0x00000001 -> 0x00000003\n"
+ "deleted: 245[0].248[1]: 0x00000002\n"
+ "added: 247[0]: { ... }\n"
+ "deleted: 249[0]: { ... }\n"
+ "added: 73882[0]: \"test 123\"\n",
+ RunUn());
+}
+
+TEST_F(ComparisonTest, EquivalentIgnoresUnknown) {
+ unittest::ForeignMessage message1, message2;
+
+ message1.set_c(5);
+ message1.mutable_unknown_fields()->AddVarint(123, 456);
+ message2.set_c(5);
+ message2.mutable_unknown_fields()->AddVarint(321, 654);
+
+ EXPECT_FALSE(util::MessageDifferencer::Equals(message1, message2));
+ EXPECT_TRUE(util::MessageDifferencer::Equivalent(message1, message2));
+}
+
+class MatchingTest : public testing::Test {
+ public:
+ typedef util::MessageDifferencer MessageDifferencer;
+
+ protected:
+ MatchingTest() {
+ }
+
+ ~MatchingTest() {
+ }
+
+ string RunWithResult(MessageDifferencer* differencer,
+ const Message& msg1, const Message& msg2,
+ bool result) {
+ string output;
+ io::StringOutputStream output_stream(&output);
+ MessageDifferencer::StreamReporter reporter(&output_stream);
+ reporter.set_report_modified_aggregates(true);
+ differencer->set_report_matches(true);
+ differencer->ReportDifferencesTo(&reporter);
+ if (result) {
+ EXPECT_TRUE(differencer->Compare(msg1, msg2));
+ } else {
+ EXPECT_FALSE(differencer->Compare(msg1, msg2));
+ }
+ return output;
+ }
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MatchingTest);
+};
+
+TEST_F(MatchingTest, StreamReporterMatching) {
+ protobuf_unittest::TestField msg1, msg2;
+ msg1.set_c(72);
+ msg2.set_c(72);
+ msg1.add_rc(13);
+ msg2.add_rc(13);
+ msg1.add_rc(17);
+ msg2.add_rc(17);
+ string output;
+ MessageDifferencer differencer;
+ differencer.set_report_matches(true);
+ differencer.ReportDifferencesToString(&output);
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "matched: c : 72\n"
+ "matched: rc[0] : 13\n"
+ "matched: rc[1] : 17\n",
+ output);
+}
+
+TEST_F(MatchingTest, DontReportMatchedWhenIgnoring) {
+ protobuf_unittest::TestField msg1, msg2;
+ msg1.set_c(72);
+ msg2.set_c(72);
+ msg1.add_rc(13);
+ msg2.add_rc(13);
+ msg1.add_rc(17);
+ msg2.add_rc(17);
+ string output;
+ MessageDifferencer differencer;
+ differencer.set_report_matches(true);
+ differencer.ReportDifferencesToString(&output);
+
+ differencer.IgnoreField(msg1.GetDescriptor()->FindFieldByName("c"));
+
+ EXPECT_TRUE(differencer.Compare(msg1, msg2));
+ EXPECT_EQ(
+ "ignored: c\n"
+ "matched: rc[0] : 13\n"
+ "matched: rc[1] : 17\n",
+ output);
+}
+
+TEST_F(MatchingTest, ReportMatchedForMovedFields) {
+ protobuf_unittest::TestDiffMessage msg1, msg2;
+ protobuf_unittest::TestDiffMessage::Item* item = msg1.add_item();
+ item->set_a(53);
+ item->set_b("hello");
+ item = msg2.add_item();
+ item->set_a(27);
+ item = msg2.add_item();
+ item->set_a(53);
+ item->set_b("hello");
+ item = msg1.add_item();
+ item->set_a(27);
+ MessageDifferencer differencer;
+ const FieldDescriptor* desc;
+ desc = msg1.GetDescriptor()->FindFieldByName("item");
+ differencer.TreatAsSet(desc);
+
+ EXPECT_EQ(
+ "matched: item[0].a -> item[1].a : 53\n"
+ "matched: item[0].b -> item[1].b : \"hello\"\n"
+ "moved: item[0] -> item[1] : { a: 53 b: \"hello\" }\n"
+ "matched: item[1].a -> item[0].a : 27\n"
+ "moved: item[1] -> item[0] : { a: 27 }\n",
+ RunWithResult(&differencer, msg1, msg2, true));
+}
+
+TEST_F(MatchingTest, MatchesAppearInPostTraversalOrderForMovedFields) {
+ protobuf_unittest::TestDiffMessage msg1, msg2;
+ protobuf_unittest::TestDiffMessage::Item* item;
+ protobuf_unittest::TestField* field;
+
+ const FieldDescriptor* desc;
+ const FieldDescriptor* nested_desc;
+ const FieldDescriptor* double_nested_desc;
+ desc = msg1.GetDescriptor()->FindFieldByName("item");
+ nested_desc = desc->message_type()->FindFieldByName("rm");
+ double_nested_desc = nested_desc->message_type()->FindFieldByName("rc");
+ MessageDifferencer differencer;
+ differencer.TreatAsSet(desc);
+ differencer.TreatAsSet(nested_desc);
+ differencer.TreatAsSet(double_nested_desc);
+
+ item = msg1.add_item();
+ field = item->add_rm();
+ field->set_c(1);
+ field->add_rc(2);
+ field->add_rc(3);
+ field = item->add_rm();
+ field->set_c(4);
+ field->add_rc(5);
+ field->add_rc(6);
+ field->add_rc(7);
+ item = msg2.add_item();
+ field = item->add_rm();
+ field->set_c(4);
+ field->add_rc(7);
+ field->add_rc(6);
+ field->add_rc(5);
+ field = item->add_rm();
+ field->set_c(1);
+ field->add_rc(3);
+ field->add_rc(2);
+ item = msg1.add_item();
+ field = item->add_rm();
+ field->set_c(8);
+ field->add_rc(10);
+ field->add_rc(11);
+ field->add_rc(9);
+ item = msg2.add_item();
+ field = item->add_rm();
+ field->set_c(8);
+ field->add_rc(9);
+ field->add_rc(10);
+ field->add_rc(11);
+
+ EXPECT_EQ(
+ "matched: item[0].rm[0].c -> item[0].rm[1].c : 1\n"
+ "moved: item[0].rm[0].rc[0] -> item[0].rm[1].rc[1] : 2\n"
+ "moved: item[0].rm[0].rc[1] -> item[0].rm[1].rc[0] : 3\n"
+ "moved: item[0].rm[0] -> item[0].rm[1] : { c: 1 rc: 2 rc: 3 }\n"
+ "matched: item[0].rm[1].c -> item[0].rm[0].c : 4\n"
+ "moved: item[0].rm[1].rc[0] -> item[0].rm[0].rc[2] : 5\n"
+ "matched: item[0].rm[1].rc[1] -> item[0].rm[0].rc[1] : 6\n"
+ "moved: item[0].rm[1].rc[2] -> item[0].rm[0].rc[0] : 7\n"
+ "moved: item[0].rm[1] -> item[0].rm[0] : { c: 4 rc: 5 rc: 6 rc: 7 }\n"
+ "matched: item[0] : { rm { c: 1 rc: 2 rc: 3 }"
+ " rm { c: 4 rc: 5 rc: 6 rc: 7 } }\n"
+ "matched: item[1].rm[0].c : 8\n"
+ "moved: item[1].rm[0].rc[0] -> item[1].rm[0].rc[1] : 10\n"
+ "moved: item[1].rm[0].rc[1] -> item[1].rm[0].rc[2] : 11\n"
+ "moved: item[1].rm[0].rc[2] -> item[1].rm[0].rc[0] : 9\n"
+ "matched: item[1].rm[0] : { c: 8 rc: 10 rc: 11 rc: 9 }\n"
+ "matched: item[1] : { rm { c: 8 rc: 10 rc: 11 rc: 9 } }\n",
+ RunWithResult(&differencer, msg1, msg2, true));
+}
+
+TEST_F(MatchingTest, MatchAndModifiedInterleaveProperly) {
+ protobuf_unittest::TestDiffMessage msg1, msg2;
+ protobuf_unittest::TestDiffMessage::Item* item;
+ protobuf_unittest::TestField* field;
+
+ const FieldDescriptor* desc;
+ const FieldDescriptor* nested_key;
+ const FieldDescriptor* nested_desc;
+ const FieldDescriptor* double_nested_key;
+ const FieldDescriptor* double_nested_desc;
+ desc = msg1.GetDescriptor()->FindFieldByName("item");
+ nested_key = desc->message_type()->FindFieldByName("a");
+ nested_desc = desc->message_type()->FindFieldByName("rm");
+ double_nested_key = nested_desc->message_type()->FindFieldByName("c");
+ double_nested_desc = nested_desc->message_type()->FindFieldByName("rc");
+
+ MessageDifferencer differencer;
+ differencer.TreatAsMap(desc, nested_key);
+ differencer.TreatAsMap(nested_desc, double_nested_key);
+ differencer.TreatAsSet(double_nested_desc);
+
+ item = msg1.add_item();
+ item->set_a(1);
+ field = item->add_rm();
+ field->set_c(2);
+ field->add_rc(3);
+ field->add_rc(4);
+ field = item->add_rm();
+ field->set_c(5);
+ field->add_rc(6);
+ field->add_rc(7);
+ field->add_rc(8);
+ item = msg1.add_item();
+ item->set_a(9);
+ field = item->add_rm();
+ field->set_c(10);
+ field->add_rc(11);
+ field->add_rc(12);
+ field = item->add_rm();
+ field->set_c(13);
+
+ item = msg2.add_item();
+ item->set_a(1);
+ field = item->add_rm();
+ field->set_c(5);
+ field->add_rc(8);
+ field->add_rc(8);
+ field->add_rc(6);
+ field = item->add_rm();
+ field->set_c(3);
+ field->add_rc(2);
+ field->add_rc(4);
+ item = msg2.add_item();
+ item->set_a(9);
+ field = item->add_rm();
+ field->set_c(10);
+ field->add_rc(12);
+ field->add_rc(11);
+ field = item->add_rm();
+ field->set_c(13);
+
+ EXPECT_EQ(
+ "matched: item[0].a : 1\n"
+ "matched: item[0].rm[1].c -> item[0].rm[0].c : 5\n"
+ "moved: item[0].rm[1].rc[0] -> item[0].rm[0].rc[2] : 6\n"
+ "moved: item[0].rm[1].rc[2] -> item[0].rm[0].rc[0] : 8\n"
+ "added: item[0].rm[0].rc[1]: 8\n"
+ "deleted: item[0].rm[1].rc[1]: 7\n"
+ "modified: item[0].rm[1] -> item[0].rm[0]: { c: 5 rc: 6 rc: 7 rc: 8 } ->"
+ " { c: 5 rc: 8 rc: 8 rc: 6 }\n"
+ "added: item[0].rm[1]: { c: 3 rc: 2 rc: 4 }\n"
+ "deleted: item[0].rm[0]: { c: 2 rc: 3 rc: 4 }\n"
+ "modified: item[0]: { a: 1 rm { c: 2 rc: 3 rc: 4 }"
+ " rm { c: 5 rc: 6 rc: 7 rc: 8 } } ->"
+ " { a: 1 rm { c: 5 rc: 8 rc: 8 rc: 6 }"
+ " rm { c: 3 rc: 2 rc: 4 } }\n"
+ "matched: item[1].a : 9\n"
+ "matched: item[1].rm[0].c : 10\n"
+ "moved: item[1].rm[0].rc[0] -> item[1].rm[0].rc[1] : 11\n"
+ "moved: item[1].rm[0].rc[1] -> item[1].rm[0].rc[0] : 12\n"
+ "matched: item[1].rm[0] : { c: 10 rc: 11 rc: 12 }\n"
+ "matched: item[1].rm[1].c : 13\n"
+ "matched: item[1].rm[1] : { c: 13 }\n"
+ "matched: item[1] : { a: 9 rm { c: 10 rc: 11 rc: 12 } rm { c: 13 } }\n",
+ RunWithResult(&differencer, msg1, msg2, false));
+}
+
+TEST_F(MatchingTest, MatchingWorksWithExtensions) {
+ protobuf_unittest::TestAllExtensions msg1, msg2;
+ protobuf_unittest::TestAllTypes::NestedMessage* nested;
+ using protobuf_unittest::repeated_nested_message_extension;
+
+ const FileDescriptor* descriptor;
+ const FieldDescriptor* desc;
+ const FieldDescriptor* nested_key;
+ descriptor = msg1.GetDescriptor()->file();
+ desc = descriptor->FindExtensionByName("repeated_nested_message_extension");
+ ASSERT_FALSE(desc == NULL);
+ nested_key = desc->message_type()->FindFieldByName("bb");
+
+ MessageDifferencer differencer;
+ differencer.TreatAsMap(desc, nested_key);
+
+ nested = msg1.AddExtension(repeated_nested_message_extension);
+ nested->set_bb(7);
+ nested = msg1.AddExtension(repeated_nested_message_extension);
+ nested->set_bb(13);
+ nested = msg1.AddExtension(repeated_nested_message_extension);
+ nested->set_bb(11);
+ nested = msg2.AddExtension(repeated_nested_message_extension);
+ nested->set_bb(11);
+ nested = msg2.AddExtension(repeated_nested_message_extension);
+ nested->set_bb(13);
+ nested = msg2.AddExtension(repeated_nested_message_extension);
+ nested->set_bb(7);
+
+ EXPECT_EQ(
+ "matched: (protobuf_unittest.repeated_nested_message_extension)[0].bb ->"
+ " (protobuf_unittest.repeated_nested_message_extension)[2].bb : 7\n"
+ "moved: (protobuf_unittest.repeated_nested_message_extension)[0] ->"
+ " (protobuf_unittest.repeated_nested_message_extension)[2] :"
+ " { bb: 7 }\n"
+ "matched: (protobuf_unittest.repeated_nested_message_extension)[1].bb :"
+ " 13\n"
+ "matched: (protobuf_unittest.repeated_nested_message_extension)[1] :"
+ " { bb: 13 }\n"
+ "matched: (protobuf_unittest.repeated_nested_message_extension)[2].bb ->"
+ " (protobuf_unittest.repeated_nested_message_extension)[0].bb :"
+ " 11\n"
+ "moved: (protobuf_unittest.repeated_nested_message_extension)[2] ->"
+ " (protobuf_unittest.repeated_nested_message_extension)[0] :"
+ " { bb: 11 }\n",
+ RunWithResult(&differencer, msg1, msg2, true));
+}
+
+TEST(AnyTest, Simple) {
+ protobuf_unittest::TestField value1, value2;
+ value1.set_a(20);
+ value2.set_a(21);
+
+ protobuf_unittest::TestAny m1, m2;
+ m1.mutable_any_value()->PackFrom(value1);
+ m2.mutable_any_value()->PackFrom(value2);
+ util::MessageDifferencer message_differencer;
+ string difference_string;
+ message_differencer.ReportDifferencesToString(&difference_string);
+ EXPECT_FALSE(message_differencer.Compare(m1, m2));
+ EXPECT_EQ("modified: any_value.a: 20 -> 21\n", difference_string);
+}
+
+TEST(Anytest, TreatAsSet) {
+ protobuf_unittest::TestField value1, value2;
+ value1.set_a(20);
+ value1.set_b(30);
+ value2.set_a(20);
+ value2.set_b(31);
+
+ protobuf_unittest::TestAny m1, m2;
+ m1.add_repeated_any_value()->PackFrom(value1);
+ m1.add_repeated_any_value()->PackFrom(value2);
+ m2.add_repeated_any_value()->PackFrom(value2);
+ m2.add_repeated_any_value()->PackFrom(value1);
+
+ util::MessageDifferencer message_differencer;
+ message_differencer.TreatAsSet(GetFieldDescriptor(m1, "repeated_any_value"));
+ EXPECT_TRUE(message_differencer.Compare(m1, m2));
+}
+
+
+} // namespace
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/message_differencer_unittest.proto b/src/google/protobuf/util/message_differencer_unittest.proto
new file mode 100644
index 00000000..698775f1
--- /dev/null
+++ b/src/google/protobuf/util/message_differencer_unittest.proto
@@ -0,0 +1,74 @@
+// 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 contains messages for testing repeated field comparison
+
+syntax = "proto2";
+package protobuf_unittest;
+
+option optimize_for = SPEED;
+
+message TestField {
+ optional int32 a = 3;
+ optional int32 b = 4;
+ optional int32 c = 1;
+ repeated int32 rc = 2;
+ optional TestField m = 5;
+
+ extend TestDiffMessage {
+ optional TestField tf = 100;
+ }
+}
+
+message TestDiffMessage {
+ repeated group Item = 1 {
+ optional int32 a = 2; // Test basic repeated field comparison.
+ optional string b = 4; // Test basic repeated field comparison.
+ repeated int32 ra = 3; // Test SetOfSet Comparison.
+ repeated string rb = 5; // Test TreatAsMap when key is repeated
+ optional TestField m = 6; // Test TreatAsMap when key is a message
+ repeated TestField rm = 7; // Test TreatAsMap when key is a repeated
+ // message
+ }
+
+ optional int32 v = 13 [deprecated = true];
+ optional string w = 14;
+ optional TestField m = 15;
+ repeated int32 rv = 11; // Test for combinations
+ repeated string rw = 10; // Test for combinations
+ repeated TestField rm = 12 [deprecated = true]; // Test for combinations
+
+ extensions 100 to 199;
+}
+
diff --git a/src/google/protobuf/util/type_resolver.h b/src/google/protobuf/util/type_resolver.h
new file mode 100644
index 00000000..77d4416a
--- /dev/null
+++ b/src/google/protobuf/util/type_resolver.h
@@ -0,0 +1,75 @@
+// 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 GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__
+#define GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/status.h>
+
+
+namespace google {
+namespace protobuf {
+class Type;
+class Enum;
+} // namespace protobuf
+
+
+namespace protobuf {
+class DescriptorPool;
+namespace util {
+
+// Abstract interface for a type resovler.
+//
+// Implementations of this interface must be thread-safe.
+class LIBPROTOBUF_EXPORT TypeResolver {
+ public:
+ TypeResolver() {}
+ virtual ~TypeResolver() {}
+
+ // Resolves a type url for a message type.
+ virtual util::Status ResolveMessageType(
+ const string& type_url, google::protobuf::Type* message_type) = 0;
+
+ // Resolves a type url for an enum type.
+ virtual util::Status ResolveEnumType(const string& type_url,
+ google::protobuf::Enum* enum_type) = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeResolver);
+};
+
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__
diff --git a/src/google/protobuf/util/type_resolver_util.cc b/src/google/protobuf/util/type_resolver_util.cc
new file mode 100644
index 00000000..053a4ed7
--- /dev/null
+++ b/src/google/protobuf/util/type_resolver_util.cc
@@ -0,0 +1,212 @@
+// 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 <google/protobuf/util/type_resolver_util.h>
+
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/wrappers.pb.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace {
+using google::protobuf::BoolValue;
+using google::protobuf::Enum;
+using google::protobuf::EnumValue;
+using google::protobuf::Field;
+using google::protobuf::Option;
+using google::protobuf::Type;
+
+using util::Status;
+using util::error::INVALID_ARGUMENT;
+using util::error::NOT_FOUND;
+
+bool SplitTypeUrl(const string& type_url,
+ string* url_prefix,
+ string* message_name) {
+ size_t pos = type_url.find_last_of("/");
+ if (pos == string::npos) {
+ return false;
+ }
+ *url_prefix = type_url.substr(0, pos);
+ *message_name = type_url.substr(pos + 1);
+ return true;
+}
+
+
+class DescriptorPoolTypeResolver : public TypeResolver {
+ public:
+ DescriptorPoolTypeResolver(const string& url_prefix,
+ const DescriptorPool* pool)
+ : url_prefix_(url_prefix), pool_(pool) {
+ }
+
+ Status ResolveMessageType(const string& type_url, Type* type) {
+ string url_prefix, message_name;
+ if (!SplitTypeUrl(type_url, &url_prefix, &message_name) ||
+ url_prefix != url_prefix_) {
+ return Status(INVALID_ARGUMENT, "Failed to parse type url: " + type_url);
+ }
+ if (url_prefix != url_prefix_) {
+ return Status(INVALID_ARGUMENT,
+ "Cannot resolve types from URL: " + url_prefix);
+ }
+ const Descriptor* descriptor = pool_->FindMessageTypeByName(message_name);
+ if (descriptor == NULL) {
+ return Status(NOT_FOUND, "Cannot found the type: " + message_name);
+ }
+ ConvertDescriptor(descriptor, type);
+ return Status();
+ }
+
+ Status ResolveEnumType(const string& type_url, Enum* enum_type) {
+ string url_prefix, type_name;
+ if (!SplitTypeUrl(type_url, &url_prefix, &type_name) ||
+ url_prefix != url_prefix_) {
+ return Status(INVALID_ARGUMENT, "Failed to parse type url: " + type_url);
+ }
+ if (url_prefix != url_prefix_) {
+ return Status(INVALID_ARGUMENT,
+ "Cannot resolve types from URL: " + url_prefix);
+ }
+ const EnumDescriptor* descriptor = pool_->FindEnumTypeByName(type_name);
+ if (descriptor == NULL) {
+ return Status(NOT_FOUND, "Cannot found the type: " + type_name);
+ }
+ ConvertEnumDescriptor(descriptor, enum_type);
+ return Status();
+ }
+
+ private:
+ void ConvertDescriptor(const Descriptor* descriptor, Type* type) {
+ type->Clear();
+ type->set_name(descriptor->full_name());
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ const FieldDescriptor* field = descriptor->field(i);
+ if (field->type() == FieldDescriptor::TYPE_GROUP) {
+ // Group fields cannot be represented with Type. We discard them.
+ continue;
+ }
+ ConvertFieldDescriptor(descriptor->field(i), type->add_fields());
+ }
+ for (int i = 0; i < descriptor->oneof_decl_count(); ++i) {
+ type->add_oneofs(descriptor->oneof_decl(i)->name());
+ }
+ type->mutable_source_context()->set_file_name(descriptor->file()->name());
+ ConvertMessageOptions(descriptor->options(), type->mutable_options());
+ }
+
+ void ConvertMessageOptions(const MessageOptions& options,
+ RepeatedPtrField<Option>* output) {
+ if (options.map_entry()) {
+ Option* option = output->Add();
+ option->set_name("map_entry");
+ BoolValue value;
+ value.set_value(true);
+ option->mutable_value()->PackFrom(value);
+ }
+
+ // TODO(xiaofeng): Set other "options"?
+ }
+
+ void ConvertFieldDescriptor(const FieldDescriptor* descriptor, Field* field) {
+ field->set_kind(static_cast<Field::Kind>(descriptor->type()));
+ switch (descriptor->label()) {
+ case FieldDescriptor::LABEL_OPTIONAL:
+ field->set_cardinality(Field::CARDINALITY_OPTIONAL);
+ break;
+ case FieldDescriptor::LABEL_REPEATED:
+ field->set_cardinality(Field::CARDINALITY_REPEATED);
+ break;
+ case FieldDescriptor::LABEL_REQUIRED:
+ field->set_cardinality(Field::CARDINALITY_REQUIRED);
+ break;
+ }
+ field->set_number(descriptor->number());
+ field->set_name(descriptor->name());
+ if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
+ field->set_type_url(GetTypeUrl(descriptor->message_type()));
+ } else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
+ field->set_type_url(GetTypeUrl(descriptor->enum_type()));
+ }
+ if (descriptor->containing_oneof() != NULL) {
+ field->set_oneof_index(descriptor->containing_oneof()->index() + 1);
+ }
+ if (descriptor->is_packed()) {
+ field->set_packed(true);
+ }
+
+ // TODO(xiaofeng): Set other field "options"?
+ }
+
+ void ConvertEnumDescriptor(const EnumDescriptor* descriptor,
+ Enum* enum_type) {
+ enum_type->Clear();
+ enum_type->set_name(descriptor->full_name());
+ enum_type->mutable_source_context()->set_file_name(
+ descriptor->file()->name());
+ for (int i = 0; i < descriptor->value_count(); ++i) {
+ const EnumValueDescriptor* value_descriptor = descriptor->value(i);
+ EnumValue* value =
+ enum_type->mutable_enumvalue()->Add();
+ value->set_name(value_descriptor->name());
+ value->set_number(value_descriptor->number());
+
+ // TODO(xiaofeng): Set EnumValue options.
+ }
+ // TODO(xiaofeng): Set Enum "options".
+ }
+
+ string GetTypeUrl(const Descriptor* descriptor) {
+ return url_prefix_ + "/" + descriptor->full_name();
+ }
+
+ string GetTypeUrl(const EnumDescriptor* descriptor) {
+ return url_prefix_ + "/" + descriptor->full_name();
+ }
+
+ string url_prefix_;
+ const DescriptorPool* pool_;
+};
+
+} // namespace
+
+TypeResolver* NewTypeResolverForDescriptorPool(
+ const string& url_prefix, const DescriptorPool* pool) {
+ return new DescriptorPoolTypeResolver(url_prefix, pool);
+}
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/util/type_resolver_util.h b/src/google/protobuf/util/type_resolver_util.h
new file mode 100644
index 00000000..c0ef3c1a
--- /dev/null
+++ b/src/google/protobuf/util/type_resolver_util.h
@@ -0,0 +1,52 @@
+// 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 GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
+#define GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
+
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+namespace google {
+namespace protobuf {
+class DescriptorPool;
+namespace util {
+class TypeResolver;
+
+// Creates a TypeResolver that serves type information in the given descriptor
+// pool. Caller takes ownership of the returned TypeResolver.
+LIBPROTOBUF_EXPORT TypeResolver* NewTypeResolverForDescriptorPool(
+ const string& url_prefix, const DescriptorPool* pool);
+
+} // namespace util
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
diff --git a/src/google/protobuf/util/type_resolver_util_test.cc b/src/google/protobuf/util/type_resolver_util_test.cc
new file mode 100644
index 00000000..74b2d0da
--- /dev/null
+++ b/src/google/protobuf/util/type_resolver_util_test.cc
@@ -0,0 +1,338 @@
+// 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 <google/protobuf/util/type_resolver_util.h>
+
+#include <limits>
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+#include <vector>
+
+#include <google/protobuf/type.pb.h>
+#include <google/protobuf/wrappers.pb.h>
+#include <google/protobuf/map_unittest.pb.h>
+#include <google/protobuf/test_util.h>
+#include <google/protobuf/unittest.pb.h>
+#include <google/protobuf/util/type_resolver.h>
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace {
+using google::protobuf::Type;
+using google::protobuf::Enum;
+using google::protobuf::Field;
+using google::protobuf::Option;
+using google::protobuf::BoolValue;
+
+static const char kUrlPrefix[] = "type.googleapis.com";
+
+class DescriptorPoolTypeResolverTest : public testing::Test {
+ public:
+ DescriptorPoolTypeResolverTest() {
+ resolver_.reset(NewTypeResolverForDescriptorPool(
+ kUrlPrefix, DescriptorPool::generated_pool()));
+ }
+
+ const Field* FindField(const Type& type, const string& name) {
+ for (int i = 0; i < type.fields_size(); ++i) {
+ const Field& field = type.fields(i);
+ if (field.name() == name) {
+ return &field;
+ }
+ }
+ return NULL;
+ }
+
+ bool HasField(const Type& type, const string& name) {
+ return FindField(type, name) != NULL;
+ }
+
+ bool HasField(const Type& type, Field::Cardinality cardinality,
+ Field::Kind kind, const string& name, int number) {
+ const Field* field = FindField(type, name);
+ if (field == NULL) {
+ return false;
+ }
+ return field->cardinality() == cardinality &&
+ field->kind() == kind && field->number() == number;
+ }
+
+ bool CheckFieldTypeUrl(const Type& type, const string& name,
+ const string& type_url) {
+ const Field* field = FindField(type, name);
+ if (field == NULL) {
+ return false;
+ }
+ return field->type_url() == type_url;
+ }
+
+ bool FieldInOneof(const Type& type, const string& name,
+ const string& oneof_name) {
+ const Field* field = FindField(type, name);
+ if (field == NULL || field->oneof_index() <= 0 ||
+ field->oneof_index() > type.oneofs_size()) {
+ return false;
+ }
+ return type.oneofs(field->oneof_index() - 1) == oneof_name;
+ }
+
+ bool IsPacked(const Type& type, const string& name) {
+ const Field* field = FindField(type, name);
+ if (field == NULL) {
+ return false;
+ }
+ return field->packed();
+ }
+
+ bool EnumHasValue(const Enum& type, const string& name, int number) {
+ for (int i = 0; i < type.enumvalue_size(); ++i) {
+ if (type.enumvalue(i).name() == name &&
+ type.enumvalue(i).number() == number) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool HasBoolOption(const RepeatedPtrField<Option>& options,
+ const string& name, bool value) {
+ for (int i = 0; i < options.size(); ++i) {
+ const Option& option = options.Get(i);
+ if (option.name() == name) {
+ BoolValue bool_value;
+ if (option.value().UnpackTo(&bool_value) &&
+ bool_value.value() == value) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ string GetTypeUrl(string full_name) {
+ return kUrlPrefix + string("/") + full_name;
+ }
+
+ template<typename T>
+ string GetTypeUrl() {
+ return GetTypeUrl(T::descriptor()->full_name());
+ }
+
+ protected:
+ google::protobuf::scoped_ptr<TypeResolver> resolver_;
+};
+
+TEST_F(DescriptorPoolTypeResolverTest, TestAllTypes) {
+ Type type;
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<protobuf_unittest::TestAllTypes>(), &type).ok());
+ // Check all optional fields.
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_INT32, "optional_int32", 1));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_INT64, "optional_int64", 2));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_UINT32, "optional_uint32", 3));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_UINT64, "optional_uint64", 4));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_SINT32, "optional_sint32", 5));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_SINT64, "optional_sint64", 6));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_FIXED32, "optional_fixed32", 7));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_FIXED64, "optional_fixed64", 8));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_SFIXED32, "optional_sfixed32", 9));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_SFIXED64, "optional_sfixed64", 10));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_FLOAT, "optional_float", 11));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_DOUBLE, "optional_double", 12));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_BOOL, "optional_bool", 13));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_STRING, "optional_string", 14));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_BYTES, "optional_bytes", 15));
+
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_MESSAGE, "optional_nested_message", 18));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_MESSAGE, "optional_foreign_message", 19));
+
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "optional_nested_message",
+ GetTypeUrl<protobuf_unittest::TestAllTypes::NestedMessage>()));
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "optional_foreign_message",
+ GetTypeUrl<protobuf_unittest::ForeignMessage>()));
+
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_ENUM, "optional_nested_enum", 21));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_ENUM, "optional_foreign_enum", 22));
+
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "optional_nested_enum",
+ GetTypeUrl("protobuf_unittest.TestAllTypes.NestedEnum")));
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "optional_foreign_enum",
+ GetTypeUrl("protobuf_unittest.ForeignEnum")));
+
+ // Check all repeated fields.
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_INT32, "repeated_int32", 31));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_INT64, "repeated_int64", 32));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_UINT32, "repeated_uint32", 33));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_UINT64, "repeated_uint64", 34));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_SINT32, "repeated_sint32", 35));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_SINT64, "repeated_sint64", 36));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_FIXED32, "repeated_fixed32", 37));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_FIXED64, "repeated_fixed64", 38));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_SFIXED32, "repeated_sfixed32", 39));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_SFIXED64, "repeated_sfixed64", 40));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_FLOAT, "repeated_float", 41));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_DOUBLE, "repeated_double", 42));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_BOOL, "repeated_bool", 43));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_STRING, "repeated_string", 44));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_BYTES, "repeated_bytes", 45));
+
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_MESSAGE, "repeated_nested_message", 48));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_MESSAGE, "repeated_foreign_message", 49));
+
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "repeated_nested_message",
+ GetTypeUrl<protobuf_unittest::TestAllTypes::NestedMessage>()));
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "repeated_foreign_message",
+ GetTypeUrl<protobuf_unittest::ForeignMessage>()));
+
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_ENUM, "repeated_nested_enum", 51));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_ENUM, "repeated_foreign_enum", 52));
+
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "repeated_nested_enum",
+ GetTypeUrl("protobuf_unittest.TestAllTypes.NestedEnum")));
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "repeated_foreign_enum",
+ GetTypeUrl("protobuf_unittest.ForeignEnum")));
+
+ // Groups are discarded when converting to Type.
+ const Descriptor* descriptor = protobuf_unittest::TestAllTypes::descriptor();
+ EXPECT_TRUE(descriptor->FindFieldByName("optionalgroup") != NULL);
+ EXPECT_TRUE(descriptor->FindFieldByName("repeatedgroup") != NULL);
+ ASSERT_FALSE(HasField(type, "optionalgroup"));
+ ASSERT_FALSE(HasField(type, "repeatedgroup"));
+}
+
+TEST_F(DescriptorPoolTypeResolverTest, TestPackedField) {
+ Type type;
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<protobuf_unittest::TestPackedTypes>(), &type).ok());
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_INT32, "packed_int32", 90));
+ EXPECT_TRUE(IsPacked(type, "packed_int32"));
+}
+
+TEST_F(DescriptorPoolTypeResolverTest, TestOneof) {
+ Type type;
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<protobuf_unittest::TestAllTypes>(), &type).ok());
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_UINT32, "oneof_uint32", 111));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_MESSAGE, "oneof_nested_message", 112));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_STRING, "oneof_string", 113));
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_OPTIONAL,
+ Field::TYPE_BYTES, "oneof_bytes", 114));
+ EXPECT_TRUE(FieldInOneof(type, "oneof_uint32", "oneof_field"));
+ EXPECT_TRUE(FieldInOneof(type, "oneof_nested_message", "oneof_field"));
+ EXPECT_TRUE(FieldInOneof(type, "oneof_string", "oneof_field"));
+ EXPECT_TRUE(FieldInOneof(type, "oneof_bytes", "oneof_field"));
+}
+
+TEST_F(DescriptorPoolTypeResolverTest, TestMap) {
+ Type type;
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl<protobuf_unittest::TestMap>(), &type).ok());
+ EXPECT_TRUE(HasField(type, Field::CARDINALITY_REPEATED,
+ Field::TYPE_MESSAGE, "map_int32_int32", 1));
+ EXPECT_TRUE(CheckFieldTypeUrl(
+ type, "map_int32_int32",
+ GetTypeUrl("protobuf_unittest.TestMap.MapInt32Int32Entry")));
+
+ ASSERT_TRUE(resolver_->ResolveMessageType(
+ GetTypeUrl("protobuf_unittest.TestMap.MapInt32Int32Entry"),
+ &type).ok());
+ EXPECT_TRUE(HasBoolOption(type.options(), "map_entry", true));
+}
+
+TEST_F(DescriptorPoolTypeResolverTest, TestEnum) {
+ Enum type;
+ ASSERT_TRUE(resolver_->ResolveEnumType(
+ GetTypeUrl("protobuf_unittest.TestAllTypes.NestedEnum"), &type).ok());
+ EnumHasValue(type, "FOO", 1);
+ EnumHasValue(type, "BAR", 2);
+ EnumHasValue(type, "BAZ", 3);
+ EnumHasValue(type, "NEG", -1);
+}
+
+} // namespace
+} // namespace util
+} // namespace protobuf
+} // namespace google