From 40ee551715c3a784ea6132dbf604b0e665ca2def Mon Sep 17 00:00:00 2001 From: temporal Date: Thu, 10 Jul 2008 02:12:20 +0000 Subject: Initial checkin. --- CHANGES.txt | 3 + CONTRIBUTORS.txt | 35 + COPYING.txt | 202 + INSTALL.txt | 237 ++ Makefile.am | 117 + README.txt | 57 + autogen.sh | 27 + configure.ac | 34 + editors/README.txt | 5 + editors/proto.vim | 83 + examples/AddPerson.java | 89 + examples/ListPeople.java | 50 + examples/Makefile | 56 + examples/README.txt | 25 + examples/add_person.cc | 92 + examples/add_person.py | 58 + examples/addressbook.proto | 30 + examples/list_people.cc | 65 + examples/list_people.py | 38 + generate_descriptor_proto.sh | 35 + java/README.txt | 46 + java/pom.xml | 107 + .../java/com/google/protobuf/AbstractMessage.java | 343 ++ .../main/java/com/google/protobuf/ByteString.java | 318 ++ .../java/com/google/protobuf/CodedInputStream.java | 766 ++++ .../com/google/protobuf/CodedOutputStream.java | 775 ++++ .../main/java/com/google/protobuf/Descriptors.java | 1635 ++++++++ .../java/com/google/protobuf/DynamicMessage.java | 391 ++ .../com/google/protobuf/ExtensionRegistry.java | 237 ++ .../main/java/com/google/protobuf/FieldSet.java | 662 ++++ .../java/com/google/protobuf/GeneratedMessage.java | 1219 ++++++ .../protobuf/InvalidProtocolBufferException.java | 77 + .../src/main/java/com/google/protobuf/Message.java | 415 +++ .../main/java/com/google/protobuf/RpcCallback.java | 27 + .../main/java/com/google/protobuf/RpcChannel.java | 51 + .../java/com/google/protobuf/RpcController.java | 98 + .../src/main/java/com/google/protobuf/RpcUtil.java | 118 + .../src/main/java/com/google/protobuf/Service.java | 97 + .../main/java/com/google/protobuf/TextFormat.java | 1242 ++++++ .../protobuf/UninitializedMessageException.java | 146 + .../java/com/google/protobuf/UnknownFieldSet.java | 746 ++++ .../main/java/com/google/protobuf/WireFormat.java | 99 + .../com/google/protobuf/AbstractMessageTest.java | 362 ++ .../com/google/protobuf/CodedInputStreamTest.java | 401 ++ .../com/google/protobuf/CodedOutputStreamTest.java | 280 ++ .../java/com/google/protobuf/DescriptorsTest.java | 313 ++ .../com/google/protobuf/DynamicMessageTest.java | 120 + .../com/google/protobuf/GeneratedMessageTest.java | 246 ++ .../test/java/com/google/protobuf/MessageTest.java | 299 ++ .../test/java/com/google/protobuf/ServiceTest.java | 164 + .../test/java/com/google/protobuf/TestUtil.java | 2402 ++++++++++++ .../java/com/google/protobuf/TextFormatTest.java | 534 +++ .../com/google/protobuf/UnknownFieldSetTest.java | 315 ++ .../java/com/google/protobuf/WireFormatTest.java | 226 ++ .../com/google/protobuf/multiple_files_test.proto | 53 + m4/acx_pthread.m4 | 363 ++ m4/stl_hash.m4 | 43 + python/README.txt | 53 + python/ez_setup.py | 277 ++ python/google/__init__.py | 1 + python/google/protobuf/__init__.py | 0 python/google/protobuf/descriptor.py | 419 +++ python/google/protobuf/internal/__init__.py | 0 python/google/protobuf/internal/decoder.py | 194 + python/google/protobuf/internal/decoder_test.py | 230 ++ python/google/protobuf/internal/descriptor_test.py | 97 + python/google/protobuf/internal/encoder.py | 192 + python/google/protobuf/internal/encoder_test.py | 211 ++ python/google/protobuf/internal/generator_test.py | 84 + python/google/protobuf/internal/input_stream.py | 211 ++ .../google/protobuf/internal/input_stream_test.py | 279 ++ .../google/protobuf/internal/message_listener.py | 55 + .../google/protobuf/internal/more_extensions.proto | 44 + .../google/protobuf/internal/more_messages.proto | 37 + python/google/protobuf/internal/output_stream.py | 112 + .../google/protobuf/internal/output_stream_test.py | 162 + python/google/protobuf/internal/reflection_test.py | 1300 +++++++ .../protobuf/internal/service_reflection_test.py | 98 + python/google/protobuf/internal/test_util.py | 354 ++ .../google/protobuf/internal/text_format_test.py | 97 + python/google/protobuf/internal/wire_format.py | 222 ++ .../google/protobuf/internal/wire_format_test.py | 232 ++ python/google/protobuf/message.py | 184 + python/google/protobuf/reflection.py | 1734 +++++++++ python/google/protobuf/service.py | 194 + python/google/protobuf/service_reflection.py | 275 ++ python/google/protobuf/text_format.py | 111 + python/mox.py | 1401 +++++++ python/setup.py | 126 + python/stubout.py | 140 + src/Makefile.am | 255 ++ src/google/protobuf/compiler/code_generator.cc | 32 + src/google/protobuf/compiler/code_generator.h | 98 + .../protobuf/compiler/command_line_interface.cc | 579 +++ .../protobuf/compiler/command_line_interface.h | 210 ++ .../compiler/command_line_interface_unittest.cc | 964 +++++ .../compiler/cpp/cpp_bootstrap_unittest.cc | 135 + src/google/protobuf/compiler/cpp/cpp_enum.cc | 196 + src/google/protobuf/compiler/cpp/cpp_enum.h | 81 + src/google/protobuf/compiler/cpp/cpp_enum_field.cc | 226 ++ src/google/protobuf/compiler/cpp/cpp_enum_field.h | 84 + src/google/protobuf/compiler/cpp/cpp_extension.cc | 104 + src/google/protobuf/compiler/cpp/cpp_extension.h | 68 + src/google/protobuf/compiler/cpp/cpp_field.cc | 83 + src/google/protobuf/compiler/cpp/cpp_field.h | 127 + src/google/protobuf/compiler/cpp/cpp_file.cc | 404 ++ src/google/protobuf/compiler/cpp/cpp_file.h | 82 + src/google/protobuf/compiler/cpp/cpp_generator.cc | 135 + src/google/protobuf/compiler/cpp/cpp_generator.h | 58 + src/google/protobuf/compiler/cpp/cpp_helpers.cc | 197 + src/google/protobuf/compiler/cpp/cpp_helpers.h | 86 + src/google/protobuf/compiler/cpp/cpp_message.cc | 1445 +++++++ src/google/protobuf/compiler/cpp/cpp_message.h | 125 + .../protobuf/compiler/cpp/cpp_message_field.cc | 229 ++ .../protobuf/compiler/cpp/cpp_message_field.h | 84 + .../protobuf/compiler/cpp/cpp_primitive_field.cc | 294 ++ .../protobuf/compiler/cpp/cpp_primitive_field.h | 84 + src/google/protobuf/compiler/cpp/cpp_service.cc | 318 ++ src/google/protobuf/compiler/cpp/cpp_service.h | 104 + .../protobuf/compiler/cpp/cpp_string_field.cc | 336 ++ .../protobuf/compiler/cpp/cpp_string_field.h | 86 + .../compiler/cpp/cpp_test_bad_identifiers.proto | 87 + src/google/protobuf/compiler/cpp/cpp_unittest.cc | 835 +++++ src/google/protobuf/compiler/importer.cc | 398 ++ src/google/protobuf/compiler/importer.h | 279 ++ src/google/protobuf/compiler/importer_unittest.cc | 539 +++ src/google/protobuf/compiler/java/java_enum.cc | 189 + src/google/protobuf/compiler/java/java_enum.h | 70 + .../protobuf/compiler/java/java_enum_field.cc | 264 ++ .../protobuf/compiler/java/java_enum_field.h | 84 + .../protobuf/compiler/java/java_extension.cc | 82 + src/google/protobuf/compiler/java/java_extension.h | 58 + src/google/protobuf/compiler/java/java_field.cc | 88 + src/google/protobuf/compiler/java/java_field.h | 82 + src/google/protobuf/compiler/java/java_file.cc | 249 ++ src/google/protobuf/compiler/java/java_file.h | 78 + .../protobuf/compiler/java/java_generator.cc | 133 + src/google/protobuf/compiler/java/java_generator.h | 58 + src/google/protobuf/compiler/java/java_helpers.cc | 229 ++ src/google/protobuf/compiler/java/java_helpers.h | 102 + src/google/protobuf/compiler/java/java_message.cc | 722 ++++ src/google/protobuf/compiler/java/java_message.h | 76 + .../protobuf/compiler/java/java_message_field.cc | 302 ++ .../protobuf/compiler/java/java_message_field.h | 84 + .../protobuf/compiler/java/java_primitive_field.cc | 365 ++ .../protobuf/compiler/java/java_primitive_field.h | 84 + src/google/protobuf/compiler/java/java_service.cc | 225 ++ src/google/protobuf/compiler/java/java_service.h | 66 + src/google/protobuf/compiler/main.cc | 46 + src/google/protobuf/compiler/package_info.h | 50 + src/google/protobuf/compiler/parser.cc | 1105 ++++++ src/google/protobuf/compiler/parser.h | 301 ++ src/google/protobuf/compiler/parser_unittest.cc | 1142 ++++++ .../protobuf/compiler/python/python_generator.cc | 794 ++++ .../protobuf/compiler/python/python_generator.h | 129 + src/google/protobuf/descriptor.cc | 2838 ++++++++++++++ src/google/protobuf/descriptor.h | 1173 ++++++ src/google/protobuf/descriptor.pb.cc | 3935 ++++++++++++++++++++ src/google/protobuf/descriptor.pb.h | 2958 +++++++++++++++ src/google/protobuf/descriptor.proto | 286 ++ src/google/protobuf/descriptor_database.cc | 291 ++ src/google/protobuf/descriptor_database.h | 183 + .../protobuf/descriptor_database_unittest.cc | 601 +++ src/google/protobuf/descriptor_unittest.cc | 2634 +++++++++++++ src/google/protobuf/dynamic_message.cc | 475 +++ src/google/protobuf/dynamic_message.h | 105 + src/google/protobuf/dynamic_message_unittest.cc | 117 + src/google/protobuf/extension_set.cc | 735 ++++ src/google/protobuf/extension_set.h | 555 +++ src/google/protobuf/extension_set_unittest.cc | 195 + .../protobuf/generated_message_reflection.cc | 665 ++++ src/google/protobuf/generated_message_reflection.h | 300 ++ .../generated_message_reflection_unittest.cc | 251 ++ src/google/protobuf/io/coded_stream.cc | 757 ++++ src/google/protobuf/io/coded_stream.h | 592 +++ src/google/protobuf/io/coded_stream_unittest.cc | 929 +++++ src/google/protobuf/io/package_info.h | 40 + src/google/protobuf/io/printer.cc | 165 + src/google/protobuf/io/printer.h | 109 + src/google/protobuf/io/printer_unittest.cc | 210 ++ src/google/protobuf/io/tokenizer.cc | 679 ++++ src/google/protobuf/io/tokenizer.h | 276 ++ src/google/protobuf/io/tokenizer_unittest.cc | 706 ++++ src/google/protobuf/io/zero_copy_stream.cc | 34 + src/google/protobuf/io/zero_copy_stream.h | 224 ++ src/google/protobuf/io/zero_copy_stream_impl.cc | 793 ++++ src/google/protobuf/io/zero_copy_stream_impl.h | 617 +++ .../protobuf/io/zero_copy_stream_unittest.cc | 443 +++ src/google/protobuf/message.cc | 345 ++ src/google/protobuf/message.h | 624 ++++ src/google/protobuf/message_unittest.cc | 224 ++ src/google/protobuf/package_info.h | 50 + src/google/protobuf/reflection_ops.cc | 241 ++ src/google/protobuf/reflection_ops.h | 74 + src/google/protobuf/reflection_ops_unittest.cc | 421 +++ src/google/protobuf/repeated_field.cc | 41 + src/google/protobuf/repeated_field.h | 782 ++++ src/google/protobuf/repeated_field_unittest.cc | 603 +++ src/google/protobuf/service.cc | 32 + src/google/protobuf/service.h | 273 ++ src/google/protobuf/stubs/common.cc | 261 ++ src/google/protobuf/stubs/common.h | 1061 ++++++ src/google/protobuf/stubs/common_unittest.cc | 319 ++ src/google/protobuf/stubs/hash.cc | 27 + src/google/protobuf/stubs/hash.h | 123 + src/google/protobuf/stubs/map-util.cc | 28 + src/google/protobuf/stubs/map-util.h | 90 + src/google/protobuf/stubs/stl_util-inl.cc | 27 + src/google/protobuf/stubs/stl_util-inl.h | 107 + src/google/protobuf/stubs/strutil.cc | 1121 ++++++ src/google/protobuf/stubs/strutil.h | 432 +++ src/google/protobuf/stubs/strutil_unittest.cc | 68 + src/google/protobuf/stubs/substitute.cc | 120 + src/google/protobuf/stubs/substitute.h | 156 + src/google/protobuf/test_util.cc | 1912 ++++++++++ src/google/protobuf/test_util.h | 118 + src/google/protobuf/testdata/golden_message | Bin 0 -> 487 bytes .../testdata/text_format_unittest_data.txt | 116 + .../text_format_unittest_extensions_data.txt | 116 + src/google/protobuf/testing/file.cc | 157 + src/google/protobuf/testing/file.h | 69 + src/google/protobuf/testing/googletest.cc | 189 + src/google/protobuf/testing/googletest.h | 81 + src/google/protobuf/text_format.cc | 941 +++++ src/google/protobuf/text_format.h | 143 + src/google/protobuf/text_format_unittest.cc | 697 ++++ src/google/protobuf/unittest.proto | 452 +++ .../protobuf/unittest_embed_optimize_for.proto | 36 + src/google/protobuf/unittest_import.proto | 47 + src/google/protobuf/unittest_mset.proto | 58 + src/google/protobuf/unittest_optimize_for.proto | 38 + src/google/protobuf/unknown_field_set.cc | 112 + src/google/protobuf/unknown_field_set.h | 322 ++ src/google/protobuf/unknown_field_set_unittest.cc | 424 +++ src/google/protobuf/wire_format.cc | 801 ++++ src/google/protobuf/wire_format.h | 446 +++ src/google/protobuf/wire_format_inl.h | 371 ++ src/google/protobuf/wire_format_unittest.cc | 526 +++ src/gtest/CHANGES | 3 + src/gtest/CONTRIBUTORS | 23 + src/gtest/COPYING | 28 + src/gtest/README | 150 + src/gtest/gen_gtest_pred_impl.py | 733 ++++ src/gtest/gtest-death-test.cc | 751 ++++ src/gtest/gtest-death-test.h | 205 + src/gtest/gtest-filepath.cc | 208 ++ src/gtest/gtest-internal-inl.h | 1118 ++++++ src/gtest/gtest-message.h | 236 ++ src/gtest/gtest-port.cc | 292 ++ src/gtest/gtest-spi.h | 247 ++ src/gtest/gtest.cc | 3546 ++++++++++++++++++ src/gtest/gtest.h | 1243 +++++++ src/gtest/gtest_main.cc | 39 + src/gtest/gtest_pred_impl.h | 368 ++ src/gtest/gtest_prod.h | 58 + src/gtest/internal/gtest-death-test-internal.h | 201 + src/gtest/internal/gtest-filepath.h | 168 + src/gtest/internal/gtest-internal.h | 569 +++ src/gtest/internal/gtest-port.h | 596 +++ src/gtest/internal/gtest-string.h | 280 ++ vsprojects/config.h | 21 + vsprojects/convert2008to2005.sh | 20 + vsprojects/extract_includes.bat | 36 + vsprojects/libprotobuf.vcproj | 408 ++ vsprojects/libprotoc.vcproj | 396 ++ vsprojects/protobuf.sln | 50 + vsprojects/protoc.vcproj | 192 + vsprojects/readme.txt | 71 + vsprojects/tests.vcproj | 569 +++ 269 files changed, 96422 insertions(+) create mode 100644 CHANGES.txt create mode 100644 CONTRIBUTORS.txt create mode 100644 COPYING.txt create mode 100644 INSTALL.txt create mode 100644 Makefile.am create mode 100644 README.txt create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 editors/README.txt create mode 100644 editors/proto.vim create mode 100644 examples/AddPerson.java create mode 100644 examples/ListPeople.java create mode 100644 examples/Makefile create mode 100644 examples/README.txt create mode 100644 examples/add_person.cc create mode 100755 examples/add_person.py create mode 100644 examples/addressbook.proto create mode 100644 examples/list_people.cc create mode 100755 examples/list_people.py create mode 100755 generate_descriptor_proto.sh create mode 100644 java/README.txt create mode 100644 java/pom.xml create mode 100644 java/src/main/java/com/google/protobuf/AbstractMessage.java create mode 100644 java/src/main/java/com/google/protobuf/ByteString.java create mode 100644 java/src/main/java/com/google/protobuf/CodedInputStream.java create mode 100644 java/src/main/java/com/google/protobuf/CodedOutputStream.java create mode 100644 java/src/main/java/com/google/protobuf/Descriptors.java create mode 100644 java/src/main/java/com/google/protobuf/DynamicMessage.java create mode 100644 java/src/main/java/com/google/protobuf/ExtensionRegistry.java create mode 100644 java/src/main/java/com/google/protobuf/FieldSet.java create mode 100644 java/src/main/java/com/google/protobuf/GeneratedMessage.java create mode 100644 java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java create mode 100644 java/src/main/java/com/google/protobuf/Message.java create mode 100644 java/src/main/java/com/google/protobuf/RpcCallback.java create mode 100644 java/src/main/java/com/google/protobuf/RpcChannel.java create mode 100644 java/src/main/java/com/google/protobuf/RpcController.java create mode 100644 java/src/main/java/com/google/protobuf/RpcUtil.java create mode 100644 java/src/main/java/com/google/protobuf/Service.java create mode 100644 java/src/main/java/com/google/protobuf/TextFormat.java create mode 100644 java/src/main/java/com/google/protobuf/UninitializedMessageException.java create mode 100644 java/src/main/java/com/google/protobuf/UnknownFieldSet.java create mode 100644 java/src/main/java/com/google/protobuf/WireFormat.java create mode 100644 java/src/test/java/com/google/protobuf/AbstractMessageTest.java create mode 100644 java/src/test/java/com/google/protobuf/CodedInputStreamTest.java create mode 100644 java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java create mode 100644 java/src/test/java/com/google/protobuf/DescriptorsTest.java create mode 100644 java/src/test/java/com/google/protobuf/DynamicMessageTest.java create mode 100644 java/src/test/java/com/google/protobuf/GeneratedMessageTest.java create mode 100644 java/src/test/java/com/google/protobuf/MessageTest.java create mode 100644 java/src/test/java/com/google/protobuf/ServiceTest.java create mode 100644 java/src/test/java/com/google/protobuf/TestUtil.java create mode 100644 java/src/test/java/com/google/protobuf/TextFormatTest.java create mode 100644 java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java create mode 100644 java/src/test/java/com/google/protobuf/WireFormatTest.java create mode 100644 java/src/test/java/com/google/protobuf/multiple_files_test.proto create mode 100644 m4/acx_pthread.m4 create mode 100644 m4/stl_hash.m4 create mode 100644 python/README.txt create mode 100755 python/ez_setup.py create mode 100755 python/google/__init__.py create mode 100755 python/google/protobuf/__init__.py create mode 100755 python/google/protobuf/descriptor.py create mode 100755 python/google/protobuf/internal/__init__.py create mode 100755 python/google/protobuf/internal/decoder.py create mode 100755 python/google/protobuf/internal/decoder_test.py create mode 100755 python/google/protobuf/internal/descriptor_test.py create mode 100755 python/google/protobuf/internal/encoder.py create mode 100755 python/google/protobuf/internal/encoder_test.py create mode 100755 python/google/protobuf/internal/generator_test.py create mode 100755 python/google/protobuf/internal/input_stream.py create mode 100755 python/google/protobuf/internal/input_stream_test.py create mode 100755 python/google/protobuf/internal/message_listener.py create mode 100644 python/google/protobuf/internal/more_extensions.proto create mode 100644 python/google/protobuf/internal/more_messages.proto create mode 100755 python/google/protobuf/internal/output_stream.py create mode 100755 python/google/protobuf/internal/output_stream_test.py create mode 100755 python/google/protobuf/internal/reflection_test.py create mode 100755 python/google/protobuf/internal/service_reflection_test.py create mode 100755 python/google/protobuf/internal/test_util.py create mode 100755 python/google/protobuf/internal/text_format_test.py create mode 100755 python/google/protobuf/internal/wire_format.py create mode 100755 python/google/protobuf/internal/wire_format_test.py create mode 100755 python/google/protobuf/message.py create mode 100755 python/google/protobuf/reflection.py create mode 100755 python/google/protobuf/service.py create mode 100755 python/google/protobuf/service_reflection.py create mode 100755 python/google/protobuf/text_format.py create mode 100755 python/mox.py create mode 100755 python/setup.py create mode 100755 python/stubout.py create mode 100644 src/Makefile.am create mode 100644 src/google/protobuf/compiler/code_generator.cc create mode 100644 src/google/protobuf/compiler/code_generator.h create mode 100644 src/google/protobuf/compiler/command_line_interface.cc create mode 100644 src/google/protobuf/compiler/command_line_interface.h create mode 100644 src/google/protobuf/compiler/command_line_interface_unittest.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_enum.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_enum.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_enum_field.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_enum_field.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_extension.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_extension.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_field.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_field.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_file.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_file.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_generator.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_generator.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_helpers.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_helpers.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_message.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_message.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_message_field.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_message_field.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_primitive_field.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_primitive_field.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_service.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_service.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_string_field.cc create mode 100644 src/google/protobuf/compiler/cpp/cpp_string_field.h create mode 100644 src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto create mode 100644 src/google/protobuf/compiler/cpp/cpp_unittest.cc create mode 100644 src/google/protobuf/compiler/importer.cc create mode 100644 src/google/protobuf/compiler/importer.h create mode 100644 src/google/protobuf/compiler/importer_unittest.cc create mode 100644 src/google/protobuf/compiler/java/java_enum.cc create mode 100644 src/google/protobuf/compiler/java/java_enum.h create mode 100644 src/google/protobuf/compiler/java/java_enum_field.cc create mode 100644 src/google/protobuf/compiler/java/java_enum_field.h create mode 100644 src/google/protobuf/compiler/java/java_extension.cc create mode 100644 src/google/protobuf/compiler/java/java_extension.h create mode 100644 src/google/protobuf/compiler/java/java_field.cc create mode 100644 src/google/protobuf/compiler/java/java_field.h create mode 100644 src/google/protobuf/compiler/java/java_file.cc create mode 100644 src/google/protobuf/compiler/java/java_file.h create mode 100644 src/google/protobuf/compiler/java/java_generator.cc create mode 100644 src/google/protobuf/compiler/java/java_generator.h create mode 100644 src/google/protobuf/compiler/java/java_helpers.cc create mode 100644 src/google/protobuf/compiler/java/java_helpers.h create mode 100644 src/google/protobuf/compiler/java/java_message.cc create mode 100644 src/google/protobuf/compiler/java/java_message.h create mode 100644 src/google/protobuf/compiler/java/java_message_field.cc create mode 100644 src/google/protobuf/compiler/java/java_message_field.h create mode 100644 src/google/protobuf/compiler/java/java_primitive_field.cc create mode 100644 src/google/protobuf/compiler/java/java_primitive_field.h create mode 100644 src/google/protobuf/compiler/java/java_service.cc create mode 100644 src/google/protobuf/compiler/java/java_service.h create mode 100644 src/google/protobuf/compiler/main.cc create mode 100644 src/google/protobuf/compiler/package_info.h create mode 100644 src/google/protobuf/compiler/parser.cc create mode 100644 src/google/protobuf/compiler/parser.h create mode 100644 src/google/protobuf/compiler/parser_unittest.cc create mode 100644 src/google/protobuf/compiler/python/python_generator.cc create mode 100644 src/google/protobuf/compiler/python/python_generator.h create mode 100644 src/google/protobuf/descriptor.cc create mode 100644 src/google/protobuf/descriptor.h create mode 100644 src/google/protobuf/descriptor.pb.cc create mode 100644 src/google/protobuf/descriptor.pb.h create mode 100644 src/google/protobuf/descriptor.proto create mode 100644 src/google/protobuf/descriptor_database.cc create mode 100644 src/google/protobuf/descriptor_database.h create mode 100644 src/google/protobuf/descriptor_database_unittest.cc create mode 100644 src/google/protobuf/descriptor_unittest.cc create mode 100644 src/google/protobuf/dynamic_message.cc create mode 100644 src/google/protobuf/dynamic_message.h create mode 100644 src/google/protobuf/dynamic_message_unittest.cc create mode 100644 src/google/protobuf/extension_set.cc create mode 100644 src/google/protobuf/extension_set.h create mode 100644 src/google/protobuf/extension_set_unittest.cc create mode 100644 src/google/protobuf/generated_message_reflection.cc create mode 100644 src/google/protobuf/generated_message_reflection.h create mode 100644 src/google/protobuf/generated_message_reflection_unittest.cc create mode 100644 src/google/protobuf/io/coded_stream.cc create mode 100644 src/google/protobuf/io/coded_stream.h create mode 100644 src/google/protobuf/io/coded_stream_unittest.cc create mode 100644 src/google/protobuf/io/package_info.h create mode 100644 src/google/protobuf/io/printer.cc create mode 100644 src/google/protobuf/io/printer.h create mode 100644 src/google/protobuf/io/printer_unittest.cc create mode 100644 src/google/protobuf/io/tokenizer.cc create mode 100644 src/google/protobuf/io/tokenizer.h create mode 100644 src/google/protobuf/io/tokenizer_unittest.cc create mode 100644 src/google/protobuf/io/zero_copy_stream.cc create mode 100644 src/google/protobuf/io/zero_copy_stream.h create mode 100644 src/google/protobuf/io/zero_copy_stream_impl.cc create mode 100644 src/google/protobuf/io/zero_copy_stream_impl.h create mode 100644 src/google/protobuf/io/zero_copy_stream_unittest.cc create mode 100644 src/google/protobuf/message.cc create mode 100644 src/google/protobuf/message.h create mode 100644 src/google/protobuf/message_unittest.cc create mode 100644 src/google/protobuf/package_info.h create mode 100644 src/google/protobuf/reflection_ops.cc create mode 100644 src/google/protobuf/reflection_ops.h create mode 100644 src/google/protobuf/reflection_ops_unittest.cc create mode 100644 src/google/protobuf/repeated_field.cc create mode 100644 src/google/protobuf/repeated_field.h create mode 100644 src/google/protobuf/repeated_field_unittest.cc create mode 100644 src/google/protobuf/service.cc create mode 100644 src/google/protobuf/service.h create mode 100644 src/google/protobuf/stubs/common.cc create mode 100644 src/google/protobuf/stubs/common.h create mode 100644 src/google/protobuf/stubs/common_unittest.cc create mode 100644 src/google/protobuf/stubs/hash.cc create mode 100644 src/google/protobuf/stubs/hash.h create mode 100644 src/google/protobuf/stubs/map-util.cc create mode 100644 src/google/protobuf/stubs/map-util.h create mode 100644 src/google/protobuf/stubs/stl_util-inl.cc create mode 100644 src/google/protobuf/stubs/stl_util-inl.h create mode 100644 src/google/protobuf/stubs/strutil.cc create mode 100644 src/google/protobuf/stubs/strutil.h create mode 100644 src/google/protobuf/stubs/strutil_unittest.cc create mode 100644 src/google/protobuf/stubs/substitute.cc create mode 100644 src/google/protobuf/stubs/substitute.h create mode 100644 src/google/protobuf/test_util.cc create mode 100644 src/google/protobuf/test_util.h create mode 100644 src/google/protobuf/testdata/golden_message create mode 100644 src/google/protobuf/testdata/text_format_unittest_data.txt create mode 100644 src/google/protobuf/testdata/text_format_unittest_extensions_data.txt create mode 100644 src/google/protobuf/testing/file.cc create mode 100644 src/google/protobuf/testing/file.h create mode 100644 src/google/protobuf/testing/googletest.cc create mode 100644 src/google/protobuf/testing/googletest.h create mode 100644 src/google/protobuf/text_format.cc create mode 100644 src/google/protobuf/text_format.h create mode 100644 src/google/protobuf/text_format_unittest.cc create mode 100644 src/google/protobuf/unittest.proto create mode 100644 src/google/protobuf/unittest_embed_optimize_for.proto create mode 100644 src/google/protobuf/unittest_import.proto create mode 100644 src/google/protobuf/unittest_mset.proto create mode 100644 src/google/protobuf/unittest_optimize_for.proto create mode 100644 src/google/protobuf/unknown_field_set.cc create mode 100644 src/google/protobuf/unknown_field_set.h create mode 100644 src/google/protobuf/unknown_field_set_unittest.cc create mode 100644 src/google/protobuf/wire_format.cc create mode 100644 src/google/protobuf/wire_format.h create mode 100644 src/google/protobuf/wire_format_inl.h create mode 100644 src/google/protobuf/wire_format_unittest.cc create mode 100644 src/gtest/CHANGES create mode 100644 src/gtest/CONTRIBUTORS create mode 100644 src/gtest/COPYING create mode 100644 src/gtest/README create mode 100755 src/gtest/gen_gtest_pred_impl.py create mode 100644 src/gtest/gtest-death-test.cc create mode 100644 src/gtest/gtest-death-test.h create mode 100644 src/gtest/gtest-filepath.cc create mode 100644 src/gtest/gtest-internal-inl.h create mode 100644 src/gtest/gtest-message.h create mode 100644 src/gtest/gtest-port.cc create mode 100644 src/gtest/gtest-spi.h create mode 100644 src/gtest/gtest.cc create mode 100644 src/gtest/gtest.h create mode 100644 src/gtest/gtest_main.cc create mode 100644 src/gtest/gtest_pred_impl.h create mode 100644 src/gtest/gtest_prod.h create mode 100644 src/gtest/internal/gtest-death-test-internal.h create mode 100644 src/gtest/internal/gtest-filepath.h create mode 100644 src/gtest/internal/gtest-internal.h create mode 100644 src/gtest/internal/gtest-port.h create mode 100644 src/gtest/internal/gtest-string.h create mode 100644 vsprojects/config.h create mode 100755 vsprojects/convert2008to2005.sh create mode 100755 vsprojects/extract_includes.bat create mode 100644 vsprojects/libprotobuf.vcproj create mode 100644 vsprojects/libprotoc.vcproj create mode 100644 vsprojects/protobuf.sln create mode 100644 vsprojects/protoc.vcproj create mode 100644 vsprojects/readme.txt create mode 100644 vsprojects/tests.vcproj diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 00000000..853e639a --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,3 @@ +2008-07-07 version 2.0.0: + + * First public release. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 00000000..6aeea57a --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,35 @@ +This file contains a list of people who have made large contributions +to the public version of Protocol Buffers. + +Original Protocol Buffers design and implementation: + Sanjay Ghemawat + Jeff Dean + Daniel Dulitz + Craig Silverstein + Paul Haahr + Corey Anderson + (and many others) + +Proto2 C++ and Java primary author: + Kenton Varda + +Proto2 Python primary authors: + Will Robinson + Petar Petrov + +Large code contributions: + Joseph Schorr + Wenbo Zhu + +Large quantity of code reviews: + Scott Bruce + Frank Yellin + Neal Norwitz + Jeffrey Yasskin + Ambrose Feinstein + +Documentation: + Lisa Carey + +Maven packaging: + Gregory Kick diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 00000000..ce3b0949 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,237 @@ +This file contains detailed but generic information on building and +installing the C++ part of this project. For shorter instructions, +as well as instructions for compiling and installing the Java or +Python parts, see README. + +====================================================================== + +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..4e2f6515 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,117 @@ +## Process this file with automake to produce Makefile.in + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src + +EXTRA_DIST = \ + autogen.sh \ + generate_descriptor_proto.sh \ + README.txt \ + INSTALL.txt \ + COPYING.txt \ + CONTRIBUTORS.txt \ + CHANGES.txt \ + editors/README.txt \ + editors/proto.vim \ + vsprojects/config.h \ + vsprojects/extract_includes.bat \ + vsprojects/libprotobuf.vcproj \ + vsprojects/libprotoc.vcproj \ + vsprojects/protobuf.sln \ + vsprojects/protoc.vcproj \ + vsprojects/readme.txt \ + vsprojects/tests.vcproj \ + vsprojects/convert2008to2005.sh \ + examples/README.txt \ + examples/Makefile \ + examples/addressbook.proto \ + examples/add_person.cc \ + examples/list_people.cc \ + examples/AddPerson.java \ + examples/ListPeople.java \ + examples/add_person.py \ + examples/list_people.py \ + java/src/main/java/com/google/protobuf/AbstractMessage.java \ + java/src/main/java/com/google/protobuf/ByteString.java \ + java/src/main/java/com/google/protobuf/CodedInputStream.java \ + java/src/main/java/com/google/protobuf/CodedOutputStream.java \ + java/src/main/java/com/google/protobuf/Descriptors.java \ + java/src/main/java/com/google/protobuf/DynamicMessage.java \ + java/src/main/java/com/google/protobuf/ExtensionRegistry.java \ + java/src/main/java/com/google/protobuf/FieldSet.java \ + java/src/main/java/com/google/protobuf/GeneratedMessage.java \ + java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \ + java/src/main/java/com/google/protobuf/Message.java \ + java/src/main/java/com/google/protobuf/RpcCallback.java \ + java/src/main/java/com/google/protobuf/RpcChannel.java \ + java/src/main/java/com/google/protobuf/RpcController.java \ + java/src/main/java/com/google/protobuf/RpcUtil.java \ + java/src/main/java/com/google/protobuf/Service.java \ + java/src/main/java/com/google/protobuf/TextFormat.java \ + java/src/main/java/com/google/protobuf/UninitializedMessageException.java \ + java/src/main/java/com/google/protobuf/UnknownFieldSet.java \ + java/src/main/java/com/google/protobuf/WireFormat.java \ + java/src/test/java/com/google/protobuf/AbstractMessageTest.java \ + java/src/test/java/com/google/protobuf/CodedInputStreamTest.java \ + java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \ + java/src/test/java/com/google/protobuf/DescriptorsTest.java \ + java/src/test/java/com/google/protobuf/DynamicMessageTest.java \ + java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \ + java/src/test/java/com/google/protobuf/MessageTest.java \ + java/src/test/java/com/google/protobuf/ServiceTest.java \ + java/src/test/java/com/google/protobuf/TestUtil.java \ + java/src/test/java/com/google/protobuf/TextFormatTest.java \ + java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \ + java/src/test/java/com/google/protobuf/WireFormatTest.java \ + java/src/test/java/com/google/protobuf/multiple_files_test.proto \ + java/pom.xml \ + java/README.txt \ + python/google/protobuf/internal/generator_test.py \ + python/google/protobuf/internal/decoder.py \ + python/google/protobuf/internal/decoder_test.py \ + python/google/protobuf/internal/descriptor_test.py \ + python/google/protobuf/internal/encoder.py \ + python/google/protobuf/internal/encoder_test.py \ + python/google/protobuf/internal/input_stream.py \ + python/google/protobuf/internal/input_stream_test.py \ + python/google/protobuf/internal/message_listener.py \ + python/google/protobuf/internal/more_extensions.proto \ + python/google/protobuf/internal/more_messages.proto \ + python/google/protobuf/internal/output_stream.py \ + python/google/protobuf/internal/output_stream_test.py \ + python/google/protobuf/internal/reflection_test.py \ + python/google/protobuf/internal/service_reflection_test.py \ + python/google/protobuf/internal/test_util.py \ + python/google/protobuf/internal/text_format_test.py \ + python/google/protobuf/internal/wire_format.py \ + python/google/protobuf/internal/wire_format_test.py \ + python/google/protobuf/internal/__init__.py \ + python/google/protobuf/descriptor.py \ + python/google/protobuf/message.py \ + python/google/protobuf/reflection.py \ + python/google/protobuf/service.py \ + python/google/protobuf/service_reflection.py \ + python/google/protobuf/text_format.py \ + python/google/protobuf/__init__.py \ + python/google/__init__.py \ + python/ez_setup.py \ + python/setup.py \ + python/mox.py \ + python/stubout.py \ + python/README.txt + +# Deletes all the files generated by autogen.sh. +MAINTAINERCLEANFILES = \ + aclocal.m4 \ + config.guess \ + config.sub \ + configure \ + depcomp \ + install-sh \ + ltmain.sh \ + Makefile.in \ + missing \ + mkinstalldirs \ + config.h.in \ + stamp.h.in diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..75a429af --- /dev/null +++ b/README.txt @@ -0,0 +1,57 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. +http://code.google.com/apis/protocolbuffers/ + +BETA WARNING +============ + +This package is a beta. This means that API may change in an +incompatible way in the future. It's unlikely that any big changes +will be made, but we can make no promises. Expect a non-beta release +sometime in August 2008. + +C++ Installation +================ + +To build and install the C++ Protocol Buffer runtime and the Protocol +Buffer compiler (protoc) execute the following: + + $ ./configure + $ make + $ make check + $ make install + +If "make check" fails, you can still install, but it is likely that +some features of this library will not work correctly on your system. +Proceed at your own risk. + +"make install" may require superuser privileges. + +For advanced usage information on configure and make, see INSTALL. + +** Note for Solaris users ** + + Solaris 10 x86 has a bug that will make linking fail, complaining + about libstdc++.la being invalid. We have included a work-around + in this package. To use the work-around, run configure as follows: + + ./configure LDFLAGS=-L$PWD/src/solaris + + See src/solaris/libstdc++.la for more info on this bug. + +Java and Python Installation +============================ + +The Java and Python runtime libraries for Protocol Buffers are located +in the java and python directories. See the README file in each +directory for more information on how to compile and install them. +Note that both of them require you to first install the Protocol +Buffer compiler (protoc), which is part of the C++ package. + +Usage +===== + +The complete documentation for Protocol Buffers is available via the +web at: + + http://code.google.com/apis/protocolbuffers/ diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..34a4d2e3 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Run this script to generate the configure script and other files that will +# be included in the distribution. These files are not checked in because they +# are automatically generated. + +# Check that we're being run from the right directory. +if test ! -e src/google/protobuf/stubs/common.h; then + cat >&2 << __EOF__ +Could not find source code. Make sure you are running this script from the +root of the distribution tree. +__EOF__ + exit 1 +fi + +set -ex + +rm -rf autom4te.cache + +aclocal-1.9 --force -I m4 +libtoolize -c -f +autoheader -f -W all +automake-1.9 --foreign -a -c -f -W all +autoconf -f -W all,no-obsolete + +rm -rf autom4te.cache config.h.in~ +exit 0 diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..6dca1633 --- /dev/null +++ b/configure.ac @@ -0,0 +1,34 @@ +## Process this file with autoconf to produce configure. +## In general, the safest way to proceed is to run ./autogen.sh + +AC_PREREQ(2.59) + +# Note: If you change the version, you must also update it in: +# * java/pom.xml +# * python/setup.py +# * src/google/protobuf/stubs/common.h +AC_INIT(protobuf, 2.0.1-SNAPSHOT, protobuf@googlegroups.com) + +AC_CONFIG_SRCDIR(src/google/protobuf/message.cc) +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE + +# Checks for programs. +AC_PROG_CC +AC_PROG_CXX +AC_PROG_LIBTOOL +AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h inttypes.h limits.h stdlib.h unistd.h]) + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_FUNC_STRTOD +AC_CHECK_FUNCS([ftruncate memset mkdir strchr strerror strtol]) + +ACX_PTHREAD +AC_CXX_STL_HASH + +AC_OUTPUT( Makefile src/Makefile ) diff --git a/editors/README.txt b/editors/README.txt new file mode 100644 index 00000000..3e9fc79e --- /dev/null +++ b/editors/README.txt @@ -0,0 +1,5 @@ +This directory contains syntax highlighting and configuration files for editors +to properly display Protocol Buffer files. + +See each file's header comment for directions on how to use it with the +appropriate editor. diff --git a/editors/proto.vim b/editors/proto.vim new file mode 100644 index 00000000..ea010480 --- /dev/null +++ b/editors/proto.vim @@ -0,0 +1,83 @@ +" Protocol Buffers - Google's data interchange format +" Copyright 2008 Google Inc. +" +" Licensed under the Apache License, Version 2.0 (the "License"); +" you may not use this file except in compliance with the License. +" You may obtain a copy of the License at +" +" http:"www.apache.org/licenses/LICENSE-2.0 +" +" Unless required by applicable law or agreed to in writing, software +" distributed under the License is distributed on an "AS IS" BASIS, +" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +" See the License for the specific language governing permissions and +" limitations under the License. + +" This is the Vim syntax file for Google Protocol Buffers. +" +" Usage: +" +" 1. cp proto.vim ~/.vim/syntax/ +" 2. Add the following to ~/.vimrc: +" +" augroup filetype +" au! BufRead,BufNewFile *.proto setfiletype proto +" augroup end + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn case match + +syn keyword pbSyntax syntax import option +syn keyword pbStructure package message group +syn keyword pbRepeat optional required repeated +syn keyword pbDefault default +syn keyword pbExtend extend extensions to max +syn keyword pbRPC service rpc returns + +syn keyword pbType int32 int64 uint32 uint64 sint32 sint64 +syn keyword pbType fixed32 fixed64 sfixed32 sfixed64 +syn keyword pbType float double bool string bytes +syn keyword pbTypedef enum +syn keyword pbBool true false + +syn match pbInt /-\?\<\d\+\>/ +syn match pbInt /\<0[xX]\x+\>/ +syn match pbFloat /\<-\?\d*\(\.\d*\)\?/ +" TODO: .proto also supports C-style block comments; +" see /usr/share/vim/vim70/syntax/c.vim for how it's done. +syn match pbComment /\/\/.*$/ +syn region pbString start=/"/ skip=/\\"/ end=/"/ +syn region pbString start=/'/ skip=/\\'/ end=/'/ + +if version >= 508 || !exists("did_proto_syn_inits") + if version < 508 + let did_proto_syn_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + HiLink pbSyntax Include + HiLink pbStructure Structure + HiLink pbRepeat Repeat + HiLink pbDefault Keyword + HiLink pbExtend Keyword + HiLink pbRPC Keyword + HiLink pbType Type + HiLink pbTypedef Typedef + HiLink pbBool Boolean + + HiLink pbInt Number + HiLink pbFloat Float + HiLink pbComment Comment + HiLink pbString String + + delcommand HiLink +endif + +let b:current_syntax = "proto" diff --git a/examples/AddPerson.java b/examples/AddPerson.java new file mode 100644 index 00000000..4917684b --- /dev/null +++ b/examples/AddPerson.java @@ -0,0 +1,89 @@ +// See README.txt for information and build instructions. + +import com.example.tutorial.AddressBookProtos.AddressBook; +import com.example.tutorial.AddressBookProtos.Person; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintStream; + +class AddPerson { + // This function fills in a Person message based on user input. + static Person PromptForAddress(BufferedReader stdin, + PrintStream stdout) throws IOException { + Person.Builder person = Person.newBuilder(); + + stdout.print("Enter person ID: "); + person.setId(Integer.valueOf(stdin.readLine())); + + stdout.print("Enter name: "); + person.setName(stdin.readLine()); + + stdout.print("Enter email address (blank for none): "); + String email = stdin.readLine(); + if (email.length() > 0) { + person.setEmail(email); + } + + while (true) { + stdout.print("Enter a phone number (or leave blank to finish): "); + String number = stdin.readLine(); + if (number.length() == 0) { + break; + } + + Person.PhoneNumber.Builder phoneNumber = + Person.PhoneNumber.newBuilder().setNumber(number); + + stdout.print("Is this a mobile, home, or work phone? "); + String type = stdin.readLine(); + if (type.equals("mobile")) { + phoneNumber.setType(Person.PhoneType.MOBILE); + } else if (type.equals("home")) { + phoneNumber.setType(Person.PhoneType.HOME); + } else if (type.equals("work")) { + phoneNumber.setType(Person.PhoneType.WORK); + } else { + stdout.println("Unknown phone type. Using default."); + } + + person.addPhone(phoneNumber); + } + + return person.build(); + } + + // Main function: Reads the entire address book from a file, + // adds one person based on user input, then writes it back out to the same + // file. + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Usage: AddPerson ADDRESS_BOOK_FILE"); + System.exit(-1); + } + + AddressBook.Builder addressBook = AddressBook.newBuilder(); + + // Read the existing address book. + try { + FileInputStream input = new FileInputStream(args[0]); + addressBook.mergeFrom(input); + input.close(); + } catch (FileNotFoundException e) { + System.out.println(args[0] + ": File not found. Creating a new file."); + } + + // Add an address. + addressBook.addPerson( + PromptForAddress(new BufferedReader(new InputStreamReader(System.in)), + System.out)); + + // Write the new address book back to disk. + FileOutputStream output = new FileOutputStream(args[0]); + addressBook.build().writeTo(output); + output.close(); + } +} diff --git a/examples/ListPeople.java b/examples/ListPeople.java new file mode 100644 index 00000000..b2f153af --- /dev/null +++ b/examples/ListPeople.java @@ -0,0 +1,50 @@ +// See README.txt for information and build instructions. + +import com.example.tutorial.AddressBookProtos.AddressBook; +import com.example.tutorial.AddressBookProtos.Person; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintStream; + +class ListPeople { + // Iterates though all people in the AddressBook and prints info about them. + static void Print(AddressBook addressBook) { + for (Person person: addressBook.getPersonList()) { + System.out.println("Person ID: " + person.getId()); + System.out.println(" Name: " + person.getName()); + if (person.hasEmail()) { + System.out.println(" E-mail address: " + person.getEmail()); + } + + for (Person.PhoneNumber phoneNumber : person.getPhoneList()) { + switch (phoneNumber.getType()) { + case MOBILE: + System.out.print(" Mobile phone #: "); + break; + case HOME: + System.out.print(" Home phone #: "); + break; + case WORK: + System.out.print(" Work phone #: "); + break; + } + System.out.println(phoneNumber.getNumber()); + } + } + } + + // Main function: Reads the entire address book from a file and prints all + // the information inside. + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Usage: ListPeople ADDRESS_BOOK_FILE"); + System.exit(-1); + } + + // Read the existing address book. + AddressBook addressBook = + AddressBook.parseFrom(new FileInputStream(args[0])); + + Print(addressBook); + } +} diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 00000000..999ee94a --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,56 @@ +# See README.txt. + +.PHONY: all cpp java python clean + +all: cpp java python + +cpp: add_person_cpp list_people_cpp +java: add_person_java list_people_java +python: add_person_python list_people_python + +clean: + rm -f add_person_cpp list_people_cpp add_person_java list_people_java add_person_python list_people_python + rm -f javac_middleman AddPerson*.class ListPeople*.class com/example/tutorial/*.class + rm -f protoc_middleman addressbook.pb.cc addressbook.pb.h addressbook_pb2.py com/example/tutorial/AddressBookProtos.java + rm -f *.pyc + rmdir com/example/tutorial 2>/dev/null || true + rmdir com/example 2>/dev/null || true + rmdir com 2>/dev/null || true + +protoc_middleman: addressbook.proto + protoc --cpp_out=. --java_out=. --python_out=. addressbook.proto + @touch protoc_middleman + +add_person_cpp: add_person.cc protoc_middleman + c++ add_person.cc addressbook.pb.cc -lprotobuf -o add_person_cpp + +list_people_cpp: list_people.cc protoc_middleman + c++ list_people.cc addressbook.pb.cc -lprotobuf -o list_people_cpp + +javac_middleman: AddPerson.java ListPeople.java protoc_middleman + javac AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java + @touch javac_middleman + +add_person_java: javac_middleman + @echo "Writing shortcut script add_person_java..." + @echo '#! /bin/sh' > add_person_java + @echo 'java -classpath .:$$CLASSPATH AddPerson "$$@"' >> add_person_java + @chmod +x add_person_java + +list_people_java: javac_middleman + @echo "Writing shortcut script list_people_java..." + @echo '#! /bin/sh' > list_people_java + @echo 'java -classpath .:$$CLASSPATH ListPeople "$$@"' >> list_people_java + @chmod +x list_people_java + +add_person_python: add_person.py protoc_middleman + @echo "Writing shortcut script add_person_python..." + @echo '#! /bin/sh' > add_person_python + @echo './add_person.py "$$@"' >> add_person_python + @chmod +x add_person_python + +list_people_python: list_people.py protoc_middleman + @echo "Writing shortcut script list_people_python..." + @echo '#! /bin/sh' > list_people_python + @echo './list_people.py "$$@"' >> list_people_python + @chmod +x list_people_python diff --git a/examples/README.txt b/examples/README.txt new file mode 100644 index 00000000..b0d1a0c2 --- /dev/null +++ b/examples/README.txt @@ -0,0 +1,25 @@ +This directory contains example code that uses Protocol Buffers to manage an +address book. Two programs are provided, each with three different +implementations, one written in each of C++, Java, and Python. The add_person +example adds a new person to an address book, prompting the user to input +the person's information. The list_people example lists people already in the +address book. The examples use the exact same format in all three languages, +so you can, for example, use add_person_java to create an address book and then +use list_people_python to read it. + +You must install the protobuf package before you can build these. + +To build all the examples (on a unix-like system), simply run "make". This +creates the following executable files in the current directory: + add_person_cpp list_people_cpp + add_person_java list_people_java + add_person_python list_people_python + +If you only want to compile examples in one language, use "make cpp", +"make java", or "make python". + +All of these programs simply take an address book file as their parameter. +The add_person programs will create the file if it doesn't already exist. + +These examples are part of the Protocol Buffers tutorial, located at: + http://code.google.com/apis/protocolbuffers/docs/tutorials.html diff --git a/examples/add_person.cc b/examples/add_person.cc new file mode 100644 index 00000000..2aca216b --- /dev/null +++ b/examples/add_person.cc @@ -0,0 +1,92 @@ +// See README.txt for information and build instructions. + +#include +#include +#include +#include "addressbook.pb.h" +using namespace std; + +// This function fills in a Person message based on user input. +void PromptForAddress(tutorial::Person* person) { + cout << "Enter person ID number: "; + int id; + cin >> id; + person->set_id(id); + cin.ignore(256, '\n'); + + cout << "Enter name: "; + getline(cin, *person->mutable_name()); + + cout << "Enter email address (blank for none): "; + string email; + getline(cin, email); + if (!email.empty()) { + person->set_email(email); + } + + while (true) { + cout << "Enter a phone number (or leave blank to finish): "; + string number; + getline(cin, number); + if (number.empty()) { + break; + } + + tutorial::Person::PhoneNumber* phone_number = person->add_phone(); + phone_number->set_number(number); + + cout << "Is this a mobile, home, or work phone? "; + string type; + getline(cin, type); + if (type == "mobile") { + phone_number->set_type(tutorial::Person::MOBILE); + } else if (type == "home") { + phone_number->set_type(tutorial::Person::HOME); + } else if (type == "work") { + phone_number->set_type(tutorial::Person::WORK); + } else { + cout << "Unknown phone type. Using default." << endl; + } + } +} + +// Main function: Reads the entire address book from a file, +// adds one person based on user input, then writes it back out to the same +// file. +int main(int argc, char* argv[]) { + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (argc != 2) { + cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; + return -1; + } + + tutorial::AddressBook address_book; + + { + // Read the existing address book. + fstream input(argv[1], ios::in | ios::binary); + if (!input) { + cout << argv[1] << ": File not found. Creating a new file." << endl; + } else if (!address_book.ParseFromIstream(&input)) { + cerr << "Failed to parse address book." << endl; + return -1; + } + } + + // Add an address. + PromptForAddress(address_book.add_person()); + + { + // Write the new address book back to disk. + fstream output(argv[1], ios::out | ios::trunc | ios::binary); + if (!address_book.SerializeToOstream(&output)) { + cerr << "Failed to write address book." << endl; + return -1; + } + } + + return 0; +} diff --git a/examples/add_person.py b/examples/add_person.py new file mode 100755 index 00000000..78e56966 --- /dev/null +++ b/examples/add_person.py @@ -0,0 +1,58 @@ +#! /usr/bin/python + +# See README.txt for information and build instructions. + +import addressbook_pb2 +import sys + +# This function fills in a Person message based on user input. +def PromptForAddress(person): + person.id = int(raw_input("Enter person ID number: ")) + person.name = raw_input("Enter name: ") + + email = raw_input("Enter email address (blank for none): ") + if email != "": + person.email = email + + while True: + number = raw_input("Enter a phone number (or leave blank to finish): ") + if number == "": + break + + phone_number = person.phone.add() + phone_number.number = number + + type = raw_input("Is this a mobile, home, or work phone? ") + if type == "mobile": + phone_number.type = addressbook_pb2.Person.MOBILE + elif type == "home": + phone_number.type = addressbook_pb2.Person.HOME + elif type == "work": + phone_number.type = addressbook_pb2.Person.WORK + else: + print "Unknown phone type; leaving as default value." + +# Main procedure: Reads the entire address book from a file, +# adds one person based on user input, then writes it back out to the same +# file. +if len(sys.argv) != 2: + print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" + sys.exit(-1) + +address_book = addressbook_pb2.AddressBook() + +# Read the existing address book. +try: + f = open(sys.argv[1], "rb") + address_book.ParseFromString(f.read()) + f.close() +except IOError: + print sys.argv[1] + ": File not found. Creating a new file." + +# Add an address. +PromptForAddress(address_book.person.add()) + +# Write the new address book back to disk. +f = open(sys.argv[1], "wb") +f.write(address_book.SerializeToString()) +f.close() diff --git a/examples/addressbook.proto b/examples/addressbook.proto new file mode 100644 index 00000000..b14829e9 --- /dev/null +++ b/examples/addressbook.proto @@ -0,0 +1,30 @@ +// See README.txt for information and build instructions. + +package tutorial; + +option java_package = "com.example.tutorial"; +option java_outer_classname = "AddressBookProtos"; + +message Person { + required string name = 1; + required int32 id = 2; // Unique ID number for this person. + optional string email = 3; + + enum PhoneType { + MOBILE = 0; + HOME = 1; + WORK = 2; + } + + message PhoneNumber { + required string number = 1; + optional PhoneType type = 2 [default = HOME]; + } + + repeated PhoneNumber phone = 4; +} + +// Our address book file is just one of these. +message AddressBook { + repeated Person person = 1; +} diff --git a/examples/list_people.cc b/examples/list_people.cc new file mode 100644 index 00000000..bd9a583f --- /dev/null +++ b/examples/list_people.cc @@ -0,0 +1,65 @@ +// See README.txt for information and build instructions. + +#include +#include +#include +#include "addressbook.pb.h" +using namespace std; + +// Iterates though all people in the AddressBook and prints info about them. +void ListPeople(const tutorial::AddressBook& address_book) { + for (int i = 0; i < address_book.person_size(); i++) { + const tutorial::Person& person = address_book.person(i); + + cout << "Person ID: " << person.id() << endl; + cout << " Name: " << person.name() << endl; + if (person.has_email()) { + cout << " E-mail address: " << person.email() << endl; + } + + for (int j = 0; j < person.phone_size(); j++) { + const tutorial::Person::PhoneNumber& phone_number = person.phone(j); + + switch (phone_number.type()) { + case tutorial::Person::MOBILE: + cout << " Mobile phone #: "; + break; + case tutorial::Person::HOME: + cout << " Home phone #: "; + break; + case tutorial::Person::WORK: + cout << " Work phone #: "; + break; + } + cout << phone_number.number() << endl; + } + } +} + +// Main function: Reads the entire address book from a file and prints all +// the information inside. +int main(int argc, char* argv[]) { + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (argc != 2) { + cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; + return -1; + } + + tutorial::AddressBook address_book; + + { + // Read the existing address book. + fstream input(argv[1], ios::in | ios::binary); + if (!address_book.ParseFromIstream(&input)) { + cerr << "Failed to parse address book." << endl; + return -1; + } + } + + ListPeople(address_book); + + return 0; +} diff --git a/examples/list_people.py b/examples/list_people.py new file mode 100755 index 00000000..76f4bf1e --- /dev/null +++ b/examples/list_people.py @@ -0,0 +1,38 @@ +#! /usr/bin/python + +# See README.txt for information and build instructions. + +import addressbook_pb2 +import sys + +# Iterates though all people in the AddressBook and prints info about them. +def ListPeople(address_book): + for person in address_book.person: + print "Person ID:", person.id + print " Name:", person.name + if person.HasField('email'): + print " E-mail address:", person.email + + for phone_number in person.phone: + if phone_number.type == addressbook_pb2.Person.MOBILE: + print " Mobile phone #: ", + elif phone_number.type == addressbook_pb2.Person.HOME: + print " Home phone #: ", + elif phone_number.type == addressbook_pb2.Person.WORK: + print " Work phone #: ", + print phone_number.number + +# Main procedure: Reads the entire address book from a file and prints all +# the information inside. +if len(sys.argv) != 2: + print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" + sys.exit(-1) + +address_book = addressbook_pb2.AddressBook() + +# Read the existing address book. +f = open(sys.argv[1], "rb") +address_book.ParseFromString(f.read()) +f.close() + +ListPeople(address_book) diff --git a/generate_descriptor_proto.sh b/generate_descriptor_proto.sh new file mode 100755 index 00000000..53e88e67 --- /dev/null +++ b/generate_descriptor_proto.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# Run this script to regenerate descriptor.pb.{h,cc} after the protocol +# compiler changes. Since these files are compiled into the protocol compiler +# itself, they cannot be generated automatically by a make rule. "make check" +# will fail if these files do not match what the protocol compiler would +# generate. + +# Note that this will always need to be run once after running +# extract_from_google3.sh. That script initially copies descriptor.pb.{h,cc} +# over from the google3 code and fixes it up to compile outside of google3, but +# it cannot fix the encoded descriptor embedded in descriptor.pb.cc. So, once +# the protocol compiler has been built with the slightly-broken +# descriptor.pb.cc, the files must be regenerated and the compiler must be +# built again. + +if test ! -e src/google/protobuf/stubs/common.h; then + cat >&2 << __EOF__ +Could not find source code. Make sure you are running this script from the +root of the distribution tree. +__EOF__ + exit 1 +fi + +if test ! -e src/Makefile; then + cat >&2 << __EOF__ +Could not find src/Makefile. You must run ./configure (and perhaps +./autogen.sh) first. +__EOF__ + exit 1 +fi + +pushd src +make protoc && ./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:. google/protobuf/descriptor.proto +popd diff --git a/java/README.txt b/java/README.txt new file mode 100644 index 00000000..3bb69236 --- /dev/null +++ b/java/README.txt @@ -0,0 +1,46 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. + +This directory contains the Java Protocol Buffers runtime library. + +Installation +============ + +1) Install Apache Maven if you don't have it: + + http://maven.apache.org/ + +2) Build the C++ code, or obtain a binary distribution of protoc. If + you install a binary distribution, make sure that it is the same + version as this package. If in doubt, run: + + $ protoc --version + + You will need to place the protoc executable in ../src. (If you + built it yourself, it should already be there.) + +3) Run the tests: + + $ mvn test + + If some tests fail, this library may not work correctly on your + system. Continue at your own risk. + +4) Install the library into your Maven repository: + + $ mvn install + +5) If you do not use Maven to manage your own build, you can build a + .jar file to use: + + $ mvn package + + The .jar will be placed in the "target" directory. + +Usage +===== + +The complete documentation for Protocol Buffers is available via the +web at: + + http://code.google.com/apis/protocolbuffers/ diff --git a/java/pom.xml b/java/pom.xml new file mode 100644 index 00000000..76b3235c --- /dev/null +++ b/java/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + + com.google.protobuf + protobuf-java + 2.0.1-SNAPSHOT + Protocol Buffer Java API + jar + 2008 + http://code.google.com/p/protobuf + + + junit + junit + 4.4 + test + + + org.easymock + easymock + 2.2 + test + + + org.easymock + easymockclassextension + 2.2.1 + test + + + + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + maven-surefire-plugin + + + **/*Test.java + + + + + maven-antrun-plugin + + + generate-sources + generate-sources + + + + + + + + + + target/generated-sources + + + run + + + + generate-test-sources + generate-test-sources + + + + + + + + + + + + + + + target/generated-test-sources + + + run + + + + + + + diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java new file mode 100644 index 00000000..f4145190 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -0,0 +1,343 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; + +/** + * A partial implementation of the {@link Message} interface which implements + * as many methods of that interface as possible in terms of other methods. + * + * @author kenton@google.com Kenton Varda + */ +public abstract class AbstractMessage implements Message { + @SuppressWarnings("unchecked") + public boolean isInitialized() { + // Check that all required fields are present. + for (FieldDescriptor field : getDescriptorForType().getFields()) { + if (field.isRequired()) { + if (!hasField(field)) { + return false; + } + } + } + + // Check that embedded messages are initialized. + for (Map.Entry entry : getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + for (Message element : (List) entry.getValue()) { + if (!element.isInitialized()) { + return false; + } + } + } else { + if (!((Message) entry.getValue()).isInitialized()) { + return false; + } + } + } + } + + return true; + } + + public final String toString() { + return TextFormat.printToString(this); + } + + public void writeTo(CodedOutputStream output) throws IOException { + for (Map.Entry entry : getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + for (Object element : (List) entry.getValue()) { + output.writeField(field.getType(), field.getNumber(), element); + } + } else { + output.writeField(field.getType(), field.getNumber(), entry.getValue()); + } + } + + UnknownFieldSet unknownFields = getUnknownFields(); + if (getDescriptorForType().getOptions().getMessageSetWireFormat()) { + unknownFields.writeAsMessageSetTo(output); + } else { + unknownFields.writeTo(output); + } + } + + public ByteString toByteString() { + try { + ByteString.CodedBuilder out = + ByteString.newCodedBuilder(getSerializedSize()); + writeTo(out.getCodedOutput()); + return out.build(); + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public byte[] toByteArray() { + try { + byte[] result = new byte[getSerializedSize()]; + CodedOutputStream output = CodedOutputStream.newInstance(result); + writeTo(output); + output.checkNoSpaceLeft(); + return result; + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a byte array threw an IOException " + + "(should never happen).", e); + } + } + + public void writeTo(OutputStream output) throws IOException { + CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + writeTo(codedOutput); + codedOutput.flush(); + } + + private int memoizedSize = -1; + + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (Map.Entry entry : getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + for (Object element : (List) entry.getValue()) { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), element); + } + } else { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), entry.getValue()); + } + } + + UnknownFieldSet unknownFields = getUnknownFields(); + if (getDescriptorForType().getOptions().getMessageSetWireFormat()) { + size += unknownFields.getSerializedSizeAsMessageSet(); + } else { + size += unknownFields.getSerializedSize(); + } + + memoizedSize = size; + return size; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof Message)) { + return false; + } + Message otherMessage = (Message) other; + if (getDescriptorForType() != otherMessage.getDescriptorForType()) { + return false; + } + return getAllFields().equals(otherMessage.getAllFields()); + } + + @Override + public int hashCode() { + int hash = 41; + hash = (19 * hash) + getDescriptorForType().hashCode(); + hash = (53 * hash) + getAllFields().hashCode(); + return hash; + } + + // ================================================================= + + /** + * A partial implementation of the {@link Message.Builder} interface which + * implements as many methods of that interface as possible in terms of + * other methods. + */ + @SuppressWarnings("unchecked") + public static abstract class Builder + implements Message.Builder { + // The compiler produces an error if this is not declared explicitly. + public abstract BuilderType clone(); + + public BuilderType clear() { + for (Map.Entry entry : + getAllFields().entrySet()) { + clearField(entry.getKey()); + } + return (BuilderType) this; + } + + public BuilderType mergeFrom(Message other) { + if (other.getDescriptorForType() != getDescriptorForType()) { + throw new IllegalArgumentException( + "mergeFrom(Message) can only merge messages of the same type."); + } + + // Note: We don't attempt to verify that other's fields have valid + // types. Doing so would be a losing battle. We'd have to verify + // all sub-messages as well, and we'd have to make copies of all of + // them to insure that they don't change after verification (since + // the Message interface itself cannot enforce immutability of + // implementations). + // TODO(kenton): Provide a function somewhere called makeDeepCopy() + // which allows people to make secure deep copies of messages. + + for (Map.Entry entry : + other.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + for (Object element : (List)entry.getValue()) { + addRepeatedField(field, element); + } + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + Message existingValue = (Message)getField(field); + if (existingValue == existingValue.getDefaultInstanceForType()) { + setField(field, entry.getValue()); + } else { + setField(field, + existingValue.newBuilderForType() + .mergeFrom(existingValue) + .mergeFrom((Message)entry.getValue()) + .build()); + } + } else { + setField(field, entry.getValue()); + } + } + + return (BuilderType) this; + } + + public BuilderType mergeFrom(CodedInputStream input) throws IOException { + return mergeFrom(input, ExtensionRegistry.getEmptyRegistry()); + } + + public BuilderType mergeFrom(CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + UnknownFieldSet.Builder unknownFields = + UnknownFieldSet.newBuilder(getUnknownFields()); + FieldSet.mergeFrom(input, unknownFields, extensionRegistry, this); + setUnknownFields(unknownFields.build()); + return (BuilderType) this; + } + + public BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { + setUnknownFields( + UnknownFieldSet.newBuilder(getUnknownFields()) + .mergeFrom(unknownFields) + .build()); + return (BuilderType) this; + } + + public BuilderType mergeFrom(ByteString data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = data.newCodedInput(); + mergeFrom(input); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(ByteString data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = data.newCodedInput(); + mergeFrom(input, extensionRegistry); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(byte[] data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = CodedInputStream.newInstance(data); + mergeFrom(input); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom( + byte[] data, ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = CodedInputStream.newInstance(data); + mergeFrom(input, extensionRegistry); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(InputStream input) throws IOException { + CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput); + codedInput.checkLastTagWas(0); + return (BuilderType) this; + } + + public BuilderType mergeFrom(InputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput, extensionRegistry); + codedInput.checkLastTagWas(0); + return (BuilderType) this; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java new file mode 100644 index 00000000..4da03eca --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -0,0 +1,318 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.UnsupportedEncodingException; + +/** + * Immutable array of bytes. + * + * @author crazybob@google.com Bob Lee + * @author kenton@google.com Kenton Varda + */ +public final class ByteString { + private final byte[] bytes; + + private ByteString(byte[] bytes) { + this.bytes = bytes; + } + + /** + * Gets the byte at the given index. + * + * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size + */ + public byte byteAt(int index) { + return bytes[index]; + } + + /** + * Gets the number of bytes. + */ + public int size() { + return this.bytes.length; + } + + /** + * Returns {@code true} if the size is {@code 0}, {@code false} otherwise. + */ + public boolean isEmpty() { + return this.bytes.length == 0; + } + + // ================================================================= + // byte[] -> ByteString + + /** + * Empty ByteString. + */ + public static final ByteString EMPTY = new ByteString(new byte[0]); + + /** + * Copies the given bytes into a {@code ByteString}. + */ + public static ByteString copyFrom(byte[] bytes, int offset, int size) { + byte[] copy = new byte[size]; + System.arraycopy(bytes, offset, copy, 0, size); + return new ByteString(copy); + } + + /** + * Copies the given bytes into a {@code ByteString}. + */ + public static ByteString copyFrom(byte[] bytes) { + return copyFrom(bytes, 0, bytes.length); + } + + /** + * Encodes {@code text} into a sequence of bytes using the named charset + * and returns the result as a {@code ByteString}. + */ + public static ByteString copyFrom(String text, String charsetName) + throws UnsupportedEncodingException { + return new ByteString(text.getBytes(charsetName)); + } + + /** + * Encodes {@code text} into a sequence of UTF-8 bytes and returns the + * result as a {@code ByteString}. + */ + public static ByteString copyFromUtf8(String text) { + try { + return new ByteString(text.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported?", e); + } + } + + // ================================================================= + // ByteString -> byte[] + + /** + * Copies bytes into a buffer at the given offset. + * + * @param target buffer to copy into + * @param offset in the target buffer + */ + public void copyTo(byte[] target, int offset) { + System.arraycopy(bytes, 0, target, offset, bytes.length); + } + + /** + * Copies bytes into a buffer. + * + * @param target buffer to copy into + * @param sourceOffset offset within these bytes + * @param targetOffset offset within the target buffer + * @param size number of bytes to copy + */ + public void copyTo(byte[] target, int sourceOffset, int targetOffset, + int size) { + System.arraycopy(bytes, sourceOffset, target, targetOffset, size); + } + + /** + * Copies bytes to a {@code byte[]}. + */ + public byte[] toByteArray() { + int size = this.bytes.length; + byte[] copy = new byte[size]; + System.arraycopy(this.bytes, 0, copy, 0, size); + return copy; + } + + /** + * Constructs a new {@code String} by decoding the bytes using the + * specified charset. + */ + public String toString(String charsetName) + throws UnsupportedEncodingException { + return new String(this.bytes, charsetName); + } + + /** + * Constructs a new {@code String} by decoding the bytes as UTF-8. + */ + public String toStringUtf8() { + try { + return new String(this.bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported?", e); + } + } + + // ================================================================= + // equals() and hashCode() + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof ByteString)) { + return false; + } + + ByteString other = (ByteString) o; + int size = this.bytes.length; + if (size != other.bytes.length) { + return false; + } + + byte[] bytes = this.bytes; + byte[] otherBytes = other.bytes; + for (int i = 0; i < size; i++) { + if (bytes[i] != otherBytes[i]) { + return false; + } + } + + return true; + } + + volatile int hash = 0; + + @Override + public int hashCode() { + int h = this.hash; + + if (h == 0) { + byte[] bytes = this.bytes; + int size = this.bytes.length; + + h = size; + for (int i = 0; i < size; i++) { + h = h * 31 + bytes[i]; + } + if (h == 0) { + h = 1; + } + + this.hash = h; + } + + return h; + } + + // ================================================================= + // Input stream + + /** + * Creates an {@code InputStream} which can be used to read the bytes. + */ + public InputStream newInput() { + return new ByteArrayInputStream(bytes); + } + + /** + * Creates a {@link CodedInputStream} which can be used to read the bytes. + * Using this is more efficient than creating a {@link CodedInputStream} + * wrapping the result of {@link #newInput()}. + */ + public CodedInputStream newCodedInput() { + // We trust CodedInputStream not to modify the bytes, or to give anyone + // else access to them. + return CodedInputStream.newInstance(bytes); + } + + // ================================================================= + // Output stream + + /** + * Creates a new {@link Output} with the given initial capacity. + */ + public static Output newOutput(int initialCapacity) { + return new Output(new ByteArrayOutputStream(initialCapacity)); + } + + /** + * Creates a new {@link Output}. + */ + public static Output newOutput() { + return newOutput(32); + } + + /** + * Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to + * create the {@code ByteString} instance. + */ + public static final class Output extends FilterOutputStream { + private final ByteArrayOutputStream bout; + + /** + * Constructs a new output with the given initial capacity. + */ + private Output(ByteArrayOutputStream bout) { + super(bout); + this.bout = bout; + } + + /** + * Creates a {@code ByteString} instance from this {@code Output}. + */ + public ByteString toByteString() { + byte[] byteArray = bout.toByteArray(); + return new ByteString(byteArray); + } + } + + /** + * Constructs a new ByteString builder, which allows you to efficiently + * construct a {@code ByteString} by writing to a {@link CodedOutputSteam}. + * Using this is much more efficient than calling {@code newOutput()} and + * wrapping that in a {@code CodedOutputStream}. + * + *

This is package-private because it's a somewhat confusing interface. + * Users can call {@link Message#toByteString()} instead of calling this + * directly. + * + * @param size The target byte size of the {@code ByteString}. You must + * write exactly this many bytes before building the result. + */ + static CodedBuilder newCodedBuilder(int size) { + return new CodedBuilder(size); + } + + /** See {@link ByteString#newCodedBuilder(int)}. */ + static final class CodedBuilder { + private final CodedOutputStream output; + private final byte[] buffer; + + private CodedBuilder(int size) { + buffer = new byte[size]; + output = CodedOutputStream.newInstance(buffer); + } + + public ByteString build() { + output.checkNoSpaceLeft(); + + // We can be confident that the CodedOutputStream will not modify the + // underlying bytes anymore because it already wrote all of them. So, + // no need to make a copy. + return new ByteString(buffer); + } + + public CodedOutputStream getCodedOutput() { + return output; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java new file mode 100644 index 00000000..b32c349c --- /dev/null +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -0,0 +1,766 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Reads and decodes protocol message fields. + * + * This class contains two kinds of methods: methods that read specific + * protocol message constructs and field types (e.g. {@link #readTag()} and + * {@link #readInt32()}) and methods that read low-level values (e.g. + * {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading + * encoded protocol messages, you should use the former methods, but if you are + * reading some other format of your own design, use the latter. + * + * @author kenton@google.com Kenton Varda + */ +public final class CodedInputStream { + /** + * Create a new CodedInputStream wrapping the given InputStream. + */ + public static CodedInputStream newInstance(InputStream input) { + return new CodedInputStream(input); + } + + /** + * Create a new CodedInputStream wrapping the given byte array. + */ + public static CodedInputStream newInstance(byte[] buf) { + return new CodedInputStream(buf); + } + + // ----------------------------------------------------------------- + + /** + * Attempt to read a field tag, returning zero if we have reached EOF. + * Protocol message parsers use this to read tags, since a protocol message + * may legally end wherever a tag occurs, and zero is not a valid tag number. + */ + public int readTag() throws IOException { + if (bufferPos == bufferSize && !refillBuffer(false)) { + lastTag = 0; + return 0; + } + + lastTag = readRawVarint32(); + if (lastTag == 0) { + // If we actually read zero, that's not a valid tag. + throw InvalidProtocolBufferException.invalidTag(); + } + return lastTag; + } + + /** + * Verifies that the last call to readTag() returned the given tag value. + * This is used to verify that a nested group ended with the correct + * end tag. + * + * @throws InvalidProtocolBufferException {@code value} does not match the + * last tag. + */ + public void checkLastTagWas(int value) throws InvalidProtocolBufferException { + if (lastTag != value) { + throw InvalidProtocolBufferException.invalidEndTag(); + } + } + + /** + * Reads and discards a single field, given its tag value. + * + * @return {@code false} if the tag is an endgroup tag, in which case + * nothing is skipped. Otherwise, returns {@code true}. + */ + public boolean skipField(int tag) throws IOException { + switch (WireFormat.getTagWireType(tag)) { + case WireFormat.WIRETYPE_VARINT: + readInt32(); + return true; + case WireFormat.WIRETYPE_FIXED64: + readRawLittleEndian64(); + return true; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + skipRawBytes(readRawVarint32()); + return true; + case WireFormat.WIRETYPE_START_GROUP: + skipMessage(); + checkLastTagWas( + WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), + WireFormat.WIRETYPE_END_GROUP)); + return true; + case WireFormat.WIRETYPE_END_GROUP: + return false; + case WireFormat.WIRETYPE_FIXED32: + readRawLittleEndian32(); + return true; + default: + throw InvalidProtocolBufferException.invalidWireType(); + } + } + + /** + * Reads and discards an entire message. This will read either until EOF + * or until an endgroup tag, whichever comes first. + */ + public void skipMessage() throws IOException { + while (true) { + int tag = readTag(); + if (tag == 0 || !skipField(tag)) return; + } + } + + // ----------------------------------------------------------------- + + /** Read a {@code double} field value from the stream. */ + public double readDouble() throws IOException { + return Double.longBitsToDouble(readRawLittleEndian64()); + } + + /** Read a {@code float} field value from the stream. */ + public float readFloat() throws IOException { + return Float.intBitsToFloat(readRawLittleEndian32()); + } + + /** Read a {@code uint64} field value from the stream. */ + public long readUInt64() throws IOException { + return readRawVarint64(); + } + + /** Read an {@code int64} field value from the stream. */ + public long readInt64() throws IOException { + return readRawVarint64(); + } + + /** Read an {@code int32} field value from the stream. */ + public int readInt32() throws IOException { + return readRawVarint32(); + } + + /** Read a {@code fixed64} field value from the stream. */ + public long readFixed64() throws IOException { + return readRawLittleEndian64(); + } + + /** Read a {@code fixed32} field value from the stream. */ + public int readFixed32() throws IOException { + return readRawLittleEndian32(); + } + + /** Read a {@code bool} field value from the stream. */ + public boolean readBool() throws IOException { + return readRawVarint32() != 0; + } + + /** Read a {@code string} field value from the stream. */ + public String readString() throws IOException { + int size = readRawVarint32(); + if (size < bufferSize - bufferPos && size > 0) { + // Fast path: We already have the bytes in a contiguous buffer, so + // just copy directly from it. + String result = new String(buffer, bufferPos, size, "UTF-8"); + bufferPos += size; + return result; + } else { + // Slow path: Build a byte array first then copy it. + return new String(readRawBytes(size), "UTF-8"); + } + } + + /** Read a {@code group} field value from the stream. */ + public void readGroup(int fieldNumber, Message.Builder builder, + ExtensionRegistry extensionRegistry) + throws IOException { + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferException.recursionLimitExceeded(); + } + ++recursionDepth; + builder.mergeFrom(this, extensionRegistry); + checkLastTagWas( + WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); + --recursionDepth; + } + + /** + * Reads a {@code group} field value from the stream and merges it into the + * given {@link UnknownFieldSet}. + */ + public void readUnknownGroup(int fieldNumber, UnknownFieldSet.Builder builder) + throws IOException { + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferException.recursionLimitExceeded(); + } + ++recursionDepth; + builder.mergeFrom(this); + checkLastTagWas( + WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); + --recursionDepth; + } + + /** Read an embedded message field value from the stream. */ + public void readMessage(Message.Builder builder, + ExtensionRegistry extensionRegistry) + throws IOException { + int length = readRawVarint32(); + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferException.recursionLimitExceeded(); + } + int oldLimit = pushLimit(length); + ++recursionDepth; + builder.mergeFrom(this, extensionRegistry); + checkLastTagWas(0); + --recursionDepth; + popLimit(oldLimit); + } + + /** Read a {@code bytes} field value from the stream. */ + public ByteString readBytes() throws IOException { + int size = readRawVarint32(); + if (size < bufferSize - bufferPos && size > 0) { + // Fast path: We already have the bytes in a contiguous buffer, so + // just copy directly from it. + ByteString result = ByteString.copyFrom(buffer, bufferPos, size); + bufferPos += size; + return result; + } else { + // Slow path: Build a byte array first then copy it. + return ByteString.copyFrom(readRawBytes(size)); + } + } + + /** Read a {@code uint32} field value from the stream. */ + public int readUInt32() throws IOException { + return readRawVarint32(); + } + + /** + * Read an enum field value from the stream. Caller is responsible + * for converting the numeric value to an actual enum. + */ + public int readEnum() throws IOException { + return readRawVarint32(); + } + + /** Read an {@code sfixed32} field value from the stream. */ + public int readSFixed32() throws IOException { + return readRawLittleEndian32(); + } + + /** Read an {@code sfixed64} field value from the stream. */ + public long readSFixed64() throws IOException { + return readRawLittleEndian64(); + } + + /** Read an {@code sint32} field value from the stream. */ + public int readSInt32() throws IOException { + return decodeZigZag32(readRawVarint32()); + } + + /** Read an {@code sint64} field value from the stream. */ + public long readSInt64() throws IOException { + return decodeZigZag64(readRawVarint64()); + } + + /** + * Read a field of any primitive type. Enums, groups, and embedded + * messages are not handled by this method. + * + * @param type Declared type of the field. + * @return An object representing the field's value, of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public Object readPrimitiveField( + Descriptors.FieldDescriptor.Type type) throws IOException { + switch (type) { + case DOUBLE : return readDouble (); + case FLOAT : return readFloat (); + case INT64 : return readInt64 (); + case UINT64 : return readUInt64 (); + case INT32 : return readInt32 (); + case FIXED64 : return readFixed64 (); + case FIXED32 : return readFixed32 (); + case BOOL : return readBool (); + case STRING : return readString (); + case BYTES : return readBytes (); + case UINT32 : return readUInt32 (); + case SFIXED32: return readSFixed32(); + case SFIXED64: return readSFixed64(); + case SINT32 : return readSInt32 (); + case SINT64 : return readSInt64 (); + + case GROUP: + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle nested groups."); + case MESSAGE: + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle embedded messages."); + case ENUM: + // We don't hanlde enums because we don't know what to do if the + // value is not recognized. + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle enums."); + } + + throw new RuntimeException( + "There is no way to get here, but the compiler thinks otherwise."); + } + + // ================================================================= + + /** + * Read a raw Varint from the stream. If larger than 32 bits, discard the + * upper bits. + */ + public int readRawVarint32() throws IOException { + byte tmp = readRawByte(); + if (tmp >= 0) { + return tmp; + } + int result = tmp & 0x7f; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = readRawByte()) << 28; + if (tmp < 0) { + // Discard upper 32 bits. + for (int i = 0; i < 5; i++) { + if (readRawByte() >= 0) return result; + } + throw InvalidProtocolBufferException.malformedVarint(); + } + } + } + } + return result; + } + + /** Read a raw Varint from the stream. */ + public long readRawVarint64() throws IOException { + int shift = 0; + long result = 0; + while (shift < 64) { + byte b = readRawByte(); + result |= (long)(b & 0x7F) << shift; + if ((b & 0x80) == 0) return result; + shift += 7; + } + throw InvalidProtocolBufferException.malformedVarint(); + } + + /** Read a 32-bit little-endian integer from the stream. */ + public int readRawLittleEndian32() throws IOException { + byte b1 = readRawByte(); + byte b2 = readRawByte(); + byte b3 = readRawByte(); + byte b4 = readRawByte(); + return (((int)b1 & 0xff) ) | + (((int)b2 & 0xff) << 8) | + (((int)b3 & 0xff) << 16) | + (((int)b4 & 0xff) << 24); + } + + /** Read a 64-bit little-endian integer from the stream. */ + public long readRawLittleEndian64() throws IOException { + byte b1 = readRawByte(); + byte b2 = readRawByte(); + byte b3 = readRawByte(); + byte b4 = readRawByte(); + byte b5 = readRawByte(); + byte b6 = readRawByte(); + byte b7 = readRawByte(); + byte b8 = readRawByte(); + return (((long)b1 & 0xff) ) | + (((long)b2 & 0xff) << 8) | + (((long)b3 & 0xff) << 16) | + (((long)b4 & 0xff) << 24) | + (((long)b5 & 0xff) << 32) | + (((long)b6 & 0xff) << 40) | + (((long)b7 & 0xff) << 48) | + (((long)b8 & 0xff) << 56); + } + + /** + * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n An unsigned 32-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + * @return A signed 32-bit integer. + */ + public static int decodeZigZag32(int n) { + return (n >>> 1) ^ -(n & 1); + } + + /** + * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n An unsigned 64-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + * @return A signed 64-bit integer. + */ + public static long decodeZigZag64(long n) { + return (n >>> 1) ^ -(n & 1); + } + + // ----------------------------------------------------------------- + + private byte[] buffer; + private int bufferSize; + private int bufferSizeAfterLimit = 0; + private int bufferPos = 0; + private InputStream input; + private int lastTag = 0; + + /** + * The total number of bytes read before the current buffer. The total + * bytes read up to the current position can be computed as + * {@code totalBytesRetired + bufferPos}. + */ + private int totalBytesRetired = 0; + + /** The absolute position of the end of the current message. */ + private int currentLimit = Integer.MAX_VALUE; + + /** See setRecursionLimit() */ + private int recursionDepth = 0; + private int recursionLimit = DEFAULT_RECURSION_LIMIT; + + /** See setSizeLimit() */ + private int sizeLimit = DEFAULT_SIZE_LIMIT; + + private static final int DEFAULT_RECURSION_LIMIT = 64; + private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB + private static final int BUFFER_SIZE = 4096; + + private CodedInputStream(byte[] buffer) { + this.buffer = buffer; + this.bufferSize = buffer.length; + this.input = null; + } + + private CodedInputStream(InputStream input) { + this.buffer = new byte[BUFFER_SIZE]; + this.bufferSize = 0; + this.input = input; + } + + /** + * Set the maximum message recursion depth. In order to prevent malicious + * messages from causing stack overflows, {@code CodedInputStream} limits + * how deeply messages may be nested. The default limit is 64. + * + * @return the old limit. + */ + public int setRecursionLimit(int limit) { + if (limit < 0) { + throw new IllegalArgumentException( + "Recursion limit cannot be negative: " + limit); + } + int oldLimit = recursionLimit; + recursionLimit = limit; + return oldLimit; + } + + /** + * Set the maximum message size. In order to prevent malicious + * messages from exhausting memory or causing integer overflows, + * {@code CodedInputStream} limits how large a message may be. + * The default limit is 64MB. You should set this limit as small + * as you can without harming your app's functionality. Note that + * size limits only apply when reading from an {@code InputStream}, not + * when constructed around a raw byte array (nor with + * {@link ByteString#newCodedInput}). + * + * @return the old limit. + */ + public int setSizeLimit(int limit) { + if (limit < 0) { + throw new IllegalArgumentException( + "Size limit cannot be negative: " + limit); + } + int oldLimit = sizeLimit; + sizeLimit = limit; + return oldLimit; + } + + /** + * Sets {@code currentLimit} to (current position) + {@code byteLimit}. This + * is called when descending into a length-delimited embedded message. + * + * @return the old limit. + */ + public int pushLimit(int byteLimit) throws InvalidProtocolBufferException { + if (byteLimit < 0) { + throw InvalidProtocolBufferException.negativeSize(); + } + byteLimit += totalBytesRetired + bufferPos; + int oldLimit = currentLimit; + if (byteLimit > oldLimit) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + currentLimit = byteLimit; + + recomputeBufferSizeAfterLimit(); + + return oldLimit; + } + + private void recomputeBufferSizeAfterLimit() { + bufferSize += bufferSizeAfterLimit; + int bufferEnd = totalBytesRetired + bufferSize; + if (bufferEnd > currentLimit) { + // Limit is in current buffer. + bufferSizeAfterLimit = bufferEnd - currentLimit; + bufferSize -= bufferSizeAfterLimit; + } else { + bufferSizeAfterLimit = 0; + } + } + + /** + * Discards the current limit, returning to the previous limit. + * + * @param oldLimit The old limit, as returned by {@code pushLimit}. + */ + public void popLimit(int oldLimit) { + currentLimit = oldLimit; + recomputeBufferSizeAfterLimit(); + } + + /** + * Called with {@code this.buffer} is empty to read more bytes from the + * input. If {@code mustSucceed} is true, refillBuffer() gurantees that + * either there will be at least one byte in the buffer when it returns + * or it will throw an exception. If {@code mustSucceed} is false, + * refillBuffer() returns false if no more bytes were available. + */ + private boolean refillBuffer(boolean mustSucceed) throws IOException { + if (bufferPos < bufferSize) { + throw new IllegalStateException( + "refillBuffer() called when buffer wasn't empty."); + } + + if (totalBytesRetired + bufferSize == currentLimit) { + // Oops, we hit a limit. + if (mustSucceed) { + throw InvalidProtocolBufferException.truncatedMessage(); + } else { + return false; + } + } + + totalBytesRetired += bufferSize; + + bufferPos = 0; + bufferSize = (input == null) ? -1 : input.read(buffer); + if (bufferSize == -1) { + bufferSize = 0; + if (mustSucceed) { + throw InvalidProtocolBufferException.truncatedMessage(); + } else { + return false; + } + } else { + recomputeBufferSizeAfterLimit(); + int totalBytesRead = + totalBytesRetired + bufferSize + bufferSizeAfterLimit; + if (totalBytesRead > sizeLimit || totalBytesRead < 0) { + throw InvalidProtocolBufferException.sizeLimitExceeded(); + } + return true; + } + } + + /** + * Read one byte from the input. + * + * @throws InvalidProtocolBufferException The end of the stream or the current + * limit was reached. + */ + public byte readRawByte() throws IOException { + if (bufferPos == bufferSize) { + refillBuffer(true); + } + return buffer[bufferPos++]; + } + + /** + * Read a fixed size of bytes from the input. + * + * @throws InvalidProtocolBufferException The end of the stream or the current + * limit was reached. + */ + public byte[] readRawBytes(int size) throws IOException { + if (size < 0) { + throw InvalidProtocolBufferException.negativeSize(); + } + + if (totalBytesRetired + bufferPos + size > currentLimit) { + // Read to the end of the stream anyway. + skipRawBytes(currentLimit - totalBytesRetired - bufferPos); + // Then fail. + throw InvalidProtocolBufferException.truncatedMessage(); + } + + if (size <= bufferSize - bufferPos) { + // We have all the bytes we need already. + byte[] bytes = new byte[size]; + System.arraycopy(buffer, bufferPos, bytes, 0, size); + bufferPos += size; + return bytes; + } else if (size < BUFFER_SIZE) { + // Reading more bytes than are in the buffer, but not an excessive number + // of bytes. We can safely allocate the resulting array ahead of time. + + // First copy what we have. + byte[] bytes = new byte[size]; + int pos = bufferSize - bufferPos; + System.arraycopy(buffer, bufferPos, bytes, 0, pos); + bufferPos = bufferSize; + + // We want to use refillBuffer() and then copy from the buffer into our + // byte array rather than reading directly into our byte array because + // the input may be unbuffered. + refillBuffer(true); + + while (size - pos > bufferSize) { + System.arraycopy(buffer, 0, bytes, pos, bufferSize); + pos += bufferSize; + bufferPos = bufferSize; + refillBuffer(true); + } + + System.arraycopy(buffer, 0, bytes, pos, size - pos); + bufferPos = size - pos; + + return bytes; + } else { + // The size is very large. For security reasons, we can't allocate the + // entire byte array yet. The size comes directly from the input, so a + // maliciously-crafted message could provide a bogus very large size in + // order to trick the app into allocating a lot of memory. We avoid this + // by allocating and reading only a small chunk at a time, so that the + // malicious message must actually *be* extremely large to cause + // problems. Meanwhile, we limit the allowed size of a message elsewhere. + + // Remember the buffer markers since we'll have to copy the bytes out of + // it later. + int originalBufferPos = bufferPos; + int originalBufferSize = bufferSize; + + // Mark the current buffer consumed. + totalBytesRetired += bufferSize; + bufferPos = 0; + bufferSize = 0; + + // Read all the rest of the bytes we need. + int sizeLeft = size - (originalBufferSize - originalBufferPos); + List chunks = new ArrayList(); + + while (sizeLeft > 0) { + byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)]; + int pos = 0; + while (pos < chunk.length) { + int n = (input == null) ? -1 : + input.read(chunk, pos, chunk.length - pos); + if (n == -1) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + totalBytesRetired += n; + pos += n; + } + sizeLeft -= chunk.length; + chunks.add(chunk); + } + + // OK, got everything. Now concatenate it all into one buffer. + byte[] bytes = new byte[size]; + + // Start by copying the leftover bytes from this.buffer. + int pos = originalBufferSize - originalBufferPos; + System.arraycopy(buffer, originalBufferPos, bytes, 0, pos); + + // And now all the chunks. + for (byte[] chunk : chunks) { + System.arraycopy(chunk, 0, bytes, pos, chunk.length); + pos += chunk.length; + } + + // Done. + return bytes; + } + } + + /** + * Reads and discards {@code size} bytes. + * + * @throws InvalidProtocolBufferException The end of the stream or the current + * limit was reached. + */ + public void skipRawBytes(int size) throws IOException { + if (size < 0) { + throw InvalidProtocolBufferException.negativeSize(); + } + + if (totalBytesRetired + bufferPos + size > currentLimit) { + // Read to the end of the stream anyway. + skipRawBytes(currentLimit - totalBytesRetired - bufferPos); + // Then fail. + throw InvalidProtocolBufferException.truncatedMessage(); + } + + if (size < bufferSize - bufferPos) { + // We have all the bytes we need already. + bufferPos += size; + } else { + // Skipping more bytes than are in the buffer. First skip what we have. + int pos = bufferSize - bufferPos; + totalBytesRetired += pos; + bufferPos = 0; + bufferSize = 0; + + // Then skip directly from the InputStream for the rest. + while (pos < size) { + int n = (input == null) ? -1 : (int) input.skip(size - pos); + if (n <= 0) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + pos += n; + totalBytesRetired += n; + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java new file mode 100644 index 00000000..18498999 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -0,0 +1,775 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * Encodes and writes protocol message fields. + * + *

This class contains two kinds of methods: methods that write specific + * protocol message constructs and field types (e.g. {@link #writeTag} and + * {@link #writeInt32}) and methods that write low-level values (e.g. + * {@link #writeRawVarint32} and {@link #writeRawBytes}). If you are + * writing encoded protocol messages, you should use the former methods, but if + * you are writing some other format of your own design, use the latter. + * + *

This class is totally unsynchronized. + * + * @author kneton@google.com Kenton Varda + */ +public final class CodedOutputStream { + private final byte[] buffer; + private final int limit; + private int position; + + private final OutputStream output; + + /** + * The buffer size used in {@link #newInstance(java.io.OutputStream)}. + */ + public static final int DEFAULT_BUFFER_SIZE = 4096; + + private CodedOutputStream(byte[] buffer, int offset, int length) { + this.output = null; + this.buffer = buffer; + this.position = offset; + this.limit = offset + length; + } + + private CodedOutputStream(OutputStream output, byte[] buffer) { + this.output = output; + this.buffer = buffer; + this.position = 0; + this.limit = buffer.length; + } + + /** + * Create a new {@code CodedOutputStream} wrapping the given + * {@code OutputStream}. + */ + public static CodedOutputStream newInstance(OutputStream output) { + return newInstance(output, DEFAULT_BUFFER_SIZE); + } + + /** + * Create a new {@code CodedOutputStream} wrapping the given + * {@code OutputStream} with a given buffer size. + */ + public static CodedOutputStream newInstance(OutputStream output, + int bufferSize) { + return new CodedOutputStream(output, new byte[bufferSize]); + } + + /** + * Create a new {@code CodedOutputStream} that writes directly to the given + * byte array. If more bytes are written than fit in the array, + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat + * array is faster than writing to an {@code OutputStream}. See also + * {@link ByteString#newCodedBuilder}. + */ + public static CodedOutputStream newInstance(byte[] flatArray) { + return newInstance(flatArray, 0, flatArray.length); + } + + /** + * Create a new {@code CodedOutputStream} that writes directly to the given + * byte array slice. If more bytes are written than fit in the slice, + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat + * array is faster than writing to an {@code OutputStream}. See also + * {@link ByteString#newCodedBuilder}. + */ + public static CodedOutputStream newInstance(byte[] flatArray, int offset, + int length) { + return new CodedOutputStream(flatArray, offset, length); + } + + // ----------------------------------------------------------------- + + /** Write a {@code double} field, including tag, to the stream. */ + public void writeDouble(int fieldNumber, double value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(Double.doubleToRawLongBits(value)); + } + + /** Write a {@code float} field, including tag, to the stream. */ + public void writeFloat(int fieldNumber, float value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(Float.floatToRawIntBits(value)); + } + + /** Write a {@code uint64} field, including tag, to the stream. */ + public void writeUInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(value); + } + + /** Write an {@code int64} field, including tag, to the stream. */ + public void writeInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(value); + } + + /** Write an {@code int32} field, including tag, to the stream. */ + public void writeInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + if (value >= 0) { + writeRawVarint32(value); + } else { + // Must sign-extend. + writeRawVarint64(value); + } + } + + /** Write a {@code fixed64} field, including tag, to the stream. */ + public void writeFixed64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(value); + } + + /** Write a {@code fixed32} field, including tag, to the stream. */ + public void writeFixed32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(value); + } + + /** Write a {@code bool} field, including tag, to the stream. */ + public void writeBool(int fieldNumber, boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawByte(value ? 1 : 0); + } + + /** Write a {@code string} field, including tag, to the stream. */ + public void writeString(int fieldNumber, String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + // Unfortunately there does not appear to be any way to tell Java to encode + // UTF-8 directly into our buffer, so we have to let it create its own byte + // array and then copy. + byte[] bytes = value.getBytes("UTF-8"); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code group} field, including tag, to the stream. */ + public void writeGroup(int fieldNumber, Message value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + value.writeTo(this); + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + } + + /** Write a group represented by an {@link UnknownFieldSet}. */ + public void writeUnknownGroup(int fieldNumber, UnknownFieldSet value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + value.writeTo(this); + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + } + + /** Write an embedded message field, including tag, to the stream. */ + public void writeMessage(int fieldNumber, Message value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeRawVarint32(value.getSerializedSize()); + value.writeTo(this); + } + + /** Write a {@code bytes} field, including tag, to the stream. */ + public void writeBytes(int fieldNumber, ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + byte[] bytes = value.toByteArray(); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code uint32} field, including tag, to the stream. */ + public void writeUInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(value); + } + + /** + * Write an enum field, including tag, to the stream. Caller is responsible + * for converting the enum value to its numeric value. + */ + public void writeEnum(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(value); + } + + /** Write an {@code sfixed32} field, including tag, to the stream. */ + public void writeSFixed32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(value); + } + + /** Write an {@code sfixed64} field, including tag, to the stream. */ + public void writeSFixed64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(value); + } + + /** Write an {@code sint32} field, including tag, to the stream. */ + public void writeSInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(encodeZigZag32(value)); + } + + /** Write an {@code sint64} field, including tag, to the stream. */ + public void writeSInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(encodeZigZag64(value)); + } + + /** + * Write a MessageSet extension field to the stream. For historical reasons, + * the wire format differs from normal fields. + */ + public void writeMessageSetExtension(int fieldNumber, Message value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + /** + * Write an unparsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. + */ + public void writeRawMessageSetExtension(int fieldNumber, ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + /** + * Write a field of arbitrary type, including tag, to the stream. + * + * @param type The field's type. + * @param number The field's number. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public void writeField(Descriptors.FieldDescriptor.Type type, + int number, Object value) throws IOException { + switch (type) { + case DOUBLE : writeDouble (number, (Double )value); break; + case FLOAT : writeFloat (number, (Float )value); break; + case INT64 : writeInt64 (number, (Long )value); break; + case UINT64 : writeUInt64 (number, (Long )value); break; + case INT32 : writeInt32 (number, (Integer )value); break; + case FIXED64 : writeFixed64 (number, (Long )value); break; + case FIXED32 : writeFixed32 (number, (Integer )value); break; + case BOOL : writeBool (number, (Boolean )value); break; + case STRING : writeString (number, (String )value); break; + case GROUP : writeGroup (number, (Message )value); break; + case MESSAGE : writeMessage (number, (Message )value); break; + case BYTES : writeBytes (number, (ByteString)value); break; + case UINT32 : writeUInt32 (number, (Integer )value); break; + case SFIXED32: writeSFixed32(number, (Integer )value); break; + case SFIXED64: writeSFixed64(number, (Long )value); break; + case SINT32 : writeSInt32 (number, (Integer )value); break; + case SINT64 : writeSInt64 (number, (Long )value); break; + + case ENUM: + writeEnum(number, ((Descriptors.EnumValueDescriptor)value).getNumber()); + break; + } + } + + // ================================================================= + + /** + * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSize(int fieldNumber, double value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSize(int fieldNumber, float value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. + */ + public static int computeInt32Size(int fieldNumber, int value) { + if (value >= 0) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } else { + // Must sign-extend. + return computeTagSize(fieldNumber) + 10; + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field, including tag. + */ + public static int computeFixed64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field, including tag. + */ + public static int computeFixed32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bool} field, including tag. + */ + public static int computeBoolSize(int fieldNumber, boolean value) { + return computeTagSize(fieldNumber) + 1; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code string} field, including tag. + */ + public static int computeStringSize(int fieldNumber, String value) { + try { + byte[] bytes = value.getBytes("UTF-8"); + return computeTagSize(fieldNumber) + + computeRawVarint32Size(bytes.length) + + bytes.length; + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported.", e); + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field, including tag. + */ + public static int computeGroupSize(int fieldNumber, Message value) { + return computeTagSize(fieldNumber) * 2 + value.getSerializedSize(); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field represented by an {@code UnknownFieldSet}, including + * tag. + */ + public static int computeUnknownGroupSize(int fieldNumber, + UnknownFieldSet value) { + return computeTagSize(fieldNumber) * 2 + value.getSerializedSize(); + } + + /** + * Compute the number of bytes that would be needed to encode an + * embedded message field, including tag. + */ + public static int computeMessageSize(int fieldNumber, Message value) { + int size = value.getSerializedSize(); + return computeTagSize(fieldNumber) + computeRawVarint32Size(size) + size; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field, including tag. + */ + public static int computeBytesSize(int fieldNumber, ByteString value) { + return computeTagSize(fieldNumber) + + computeRawVarint32Size(value.size()) + + value.size(); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field, including tag. + */ + public static int computeUInt32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * enum field, including tag. Caller is responsible for converting the + * enum value to its numeric value. + */ + public static int computeEnumSize(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field, including tag. + */ + public static int computeSFixed32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field, including tag. + */ + public static int computeSFixed64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field, including tag. + */ + public static int computeSInt32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + + computeRawVarint32Size(encodeZigZag32(value)); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint64} field, including tag. + */ + public static int computeSInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + + computeRawVarint64Size(encodeZigZag64(value)); + } + + /** + * Compute the number of bytes that would be needed to encode a + * MessageSet extension to the stream. For historical reasons, + * the wire format differs from normal fields. + */ + public static int computeMessageSetExtensionSize( + int fieldNumber, Message value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * unparsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. + */ + public static int computeRawMessageSetExtensionSize( + int fieldNumber, ByteString value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * field of arbitrary type, including tag, to the stream. + * + * @param type The field's type. + * @param number The field's number. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public static int computeFieldSize( + Descriptors.FieldDescriptor.Type type, + int number, Object value) { + switch (type) { + case DOUBLE : return computeDoubleSize (number, (Double )value); + case FLOAT : return computeFloatSize (number, (Float )value); + case INT64 : return computeInt64Size (number, (Long )value); + case UINT64 : return computeUInt64Size (number, (Long )value); + case INT32 : return computeInt32Size (number, (Integer )value); + case FIXED64 : return computeFixed64Size (number, (Long )value); + case FIXED32 : return computeFixed32Size (number, (Integer )value); + case BOOL : return computeBoolSize (number, (Boolean )value); + case STRING : return computeStringSize (number, (String )value); + case GROUP : return computeGroupSize (number, (Message )value); + case MESSAGE : return computeMessageSize (number, (Message )value); + case BYTES : return computeBytesSize (number, (ByteString)value); + case UINT32 : return computeUInt32Size (number, (Integer )value); + case SFIXED32: return computeSFixed32Size(number, (Integer )value); + case SFIXED64: return computeSFixed64Size(number, (Long )value); + case SINT32 : return computeSInt32Size (number, (Integer )value); + case SINT64 : return computeSInt64Size (number, (Long )value); + + case ENUM: + return computeEnumSize(number, + ((Descriptors.EnumValueDescriptor)value).getNumber()); + } + + throw new RuntimeException( + "There is no way to get here, but the compiler thinks otherwise."); + } + + // ================================================================= + + /** + * Internal helper that writes the current buffer to the output. The + * buffer position is reset to its initial value when this returns. + */ + private void refreshBuffer() throws IOException { + if (output == null) { + // We're writing to a single buffer. + throw new OutOfSpaceException(); + } + + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + output.write(buffer, 0, position); + position = 0; + } + + /** + * Flushes the stream and forces any buffered bytes to be written. This + * does not flush the underlying OutputStream. + */ + public void flush() throws IOException { + if (output != null) { + refreshBuffer(); + } + } + + /** + * If writing to a flat array, return the space left in the array. + * Otherwise, throws {@code UnsupportedOperationException}. + */ + public int spaceLeft() { + if (output == null) { + return limit - position; + } else { + throw new UnsupportedOperationException( + "spaceLeft() can only be called on CodedOutputStreams that are " + + "writing to a flat array."); + } + } + + /** + * Verifies that {@link #spaceLeft()} returns zero. It's common to create + * a byte array that is exactly big enough to hold a message, then write to + * it with a {@code CodedOutputStream}. Calling {@code checkNoSpaceLeft()} + * after writing verifies that the message was actually as big as expected, + * which can help catch bugs. + */ + public void checkNoSpaceLeft() { + if (spaceLeft() != 0) { + throw new IllegalStateException( + "Did not write as much data as expected."); + } + } + + /** + * If you create a CodedOutputStream around a simple flat array, you must + * not attempt to write more bytes than the array has space. Otherwise, + * this exception will be thrown. + */ + public static class OutOfSpaceException extends IOException { + OutOfSpaceException() { + super("CodedOutputStream was writing to a flat byte array and ran " + + "out of space."); + } + } + + /** Write a single byte. */ + public void writeRawByte(byte value) throws IOException { + if (position == limit) { + refreshBuffer(); + } + + buffer[position++] = value; + } + + /** Write a single byte, represented by an integer value. */ + public void writeRawByte(int value) throws IOException { + writeRawByte((byte) value); + } + + /** Write an array of bytes. */ + public void writeRawBytes(byte[] value) throws IOException { + writeRawBytes(value, 0, value.length); + } + + /** Write part of an array of bytes. */ + public void writeRawBytes(byte[] value, int offset, int length) + throws IOException { + if (limit - position >= length) { + // We have room in the current buffer. + System.arraycopy(value, offset, buffer, position, length); + position += length; + } else { + // Write extends past current buffer. Fill the rest of this buffer and + // flush. + int bytesWritten = limit - position; + System.arraycopy(value, offset, buffer, position, bytesWritten); + offset += bytesWritten; + length -= bytesWritten; + position = limit; + refreshBuffer(); + + // Now deal with the rest. + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + if (length <= limit) { + // Fits in new buffer. + System.arraycopy(value, offset, buffer, 0, length); + position = length; + } else { + // Write is very big. Let's do it all at once. + output.write(value, offset, length); + } + } + } + + /** Encode and write a tag. */ + public void writeTag(int fieldNumber, int wireType) throws IOException { + writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); + } + + /** Compute the number of bytes that would be needed to encode a tag. */ + public static int computeTagSize(int fieldNumber) { + return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0)); + } + + /** + * Encode and write a varint. {@code value} is treated as + * unsigned, so it won't be sign-extended if negative. + */ + public void writeRawVarint32(int value) throws IOException { + while (true) { + if ((value & ~0x7F) == 0) { + writeRawByte(value); + return; + } else { + writeRawByte((value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * {@code value} is treated as unsigned, so it won't be sign-extended if + * negative. + */ + public static int computeRawVarint32Size(int value) { + if ((value & (0xffffffff << 7)) == 0) return 1; + if ((value & (0xffffffff << 14)) == 0) return 2; + if ((value & (0xffffffff << 21)) == 0) return 3; + if ((value & (0xffffffff << 28)) == 0) return 4; + return 5; + } + + /** Encode and write a varint. */ + public void writeRawVarint64(long value) throws IOException { + while (true) { + if ((value & ~0x7FL) == 0) { + writeRawByte((int)value); + return; + } else { + writeRawByte(((int)value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + /** Compute the number of bytes that would be needed to encode a varint. */ + public static int computeRawVarint64Size(long value) { + if ((value & (0xffffffffffffffffL << 7)) == 0) return 1; + if ((value & (0xffffffffffffffffL << 14)) == 0) return 2; + if ((value & (0xffffffffffffffffL << 21)) == 0) return 3; + if ((value & (0xffffffffffffffffL << 28)) == 0) return 4; + if ((value & (0xffffffffffffffffL << 35)) == 0) return 5; + if ((value & (0xffffffffffffffffL << 42)) == 0) return 6; + if ((value & (0xffffffffffffffffL << 49)) == 0) return 7; + if ((value & (0xffffffffffffffffL << 56)) == 0) return 8; + if ((value & (0xffffffffffffffffL << 63)) == 0) return 9; + return 10; + } + + /** Write a little-endian 32-bit integer. */ + public void writeRawLittleEndian32(int value) throws IOException { + writeRawByte((value ) & 0xFF); + writeRawByte((value >> 8) & 0xFF); + writeRawByte((value >> 16) & 0xFF); + writeRawByte((value >> 24) & 0xFF); + } + + public static final int LITTLE_ENDIAN_32_SIZE = 4; + + /** Write a little-endian 64-bit integer. */ + public void writeRawLittleEndian64(long value) throws IOException { + writeRawByte((int)(value ) & 0xFF); + writeRawByte((int)(value >> 8) & 0xFF); + writeRawByte((int)(value >> 16) & 0xFF); + writeRawByte((int)(value >> 24) & 0xFF); + writeRawByte((int)(value >> 32) & 0xFF); + writeRawByte((int)(value >> 40) & 0xFF); + writeRawByte((int)(value >> 48) & 0xFF); + writeRawByte((int)(value >> 56) & 0xFF); + } + + public static final int LITTLE_ENDIAN_64_SIZE = 8; + + /** + * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n A signed 32-bit integer. + * @return An unsigned 32-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + */ + public static int encodeZigZag32(int n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); + } + + /** + * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n A signed 64-bit integer. + * @return An unsigned 64-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + */ + public static long encodeZigZag64(long n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); + } +} diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java new file mode 100644 index 00000000..9ad8e521 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -0,0 +1,1635 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.DescriptorProtos.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Contains a collection of classes which describe protocol message types. + * + * Every message type has a {@link Descriptors.Descriptor}, which lists all + * its fields and other information about a type. You can get a message + * type's descriptor by calling {@code MessageType.getDescriptor()}, or + * (given a message object of the type) {@code message.getDescriptorForType()}. + * + * Descriptors are built from DescriptorProtos, as defined in + * {@code net/proto2/proto/descriptor.proto}. + * + * @author kenton@google.com Kenton Varda + */ +public final class Descriptors { + /** + * Describes a {@code .proto} file, including everything defined within. + */ + public static final class FileDescriptor { + /** Convert the descriptor to its protocol message representation. */ + public FileDescriptorProto toProto() { return proto; } + + /** Get the file name. */ + public String getName() { return proto.getName(); } + + /** + * Get the proto package name. This is the package name given by the + * {@code package} statement in the {@code .proto} file, which differs + * from the Java package. + */ + public String getPackage() { return proto.getPackage(); } + + /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */ + public FileOptions getOptions() { return proto.getOptions(); } + + /** Get a list of top-level message types declared in this file. */ + public List getMessageTypes() { + return Collections.unmodifiableList(Arrays.asList(messageTypes)); + } + + /** Get a list of top-level enum types declared in this file. */ + public List getEnumTypes() { + return Collections.unmodifiableList(Arrays.asList(enumTypes)); + } + + /** Get a list of top-level services declared in this file. */ + public List getServices() { + return Collections.unmodifiableList(Arrays.asList(services)); + } + + /** Get a list of top-level extensions declared in this file. */ + public List getExtensions() { + return Collections.unmodifiableList(Arrays.asList(extensions)); + } + + /** Get a list of this file's dependencies (imports). */ + public List getDependencies() { + return Collections.unmodifiableList(Arrays.asList(dependencies)); + } + + /** + * Find a message type in the file by name. Does not find nested types. + * + * @param name The unqualified type name to look for. + * @return The message type's descriptor, or {@code null} if not found. + */ + public Descriptor findMessageTypeByName(String name) { + // Don't allow looking up nested types. This will make optimization + // easier later. + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof Descriptor && + result.getFile() == this) { + return (Descriptor)result; + } else { + return null; + } + } + + /** + * Find an enum type in the file by name. Does not find nested types. + * + * @param name The unqualified type name to look for. + * @return The enum type's descriptor, or {@code null} if not found. + */ + public EnumDescriptor findEnumTypeByName(String name) { + // Don't allow looking up nested types. This will make optimization + // easier later. + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof EnumDescriptor && + result.getFile() == this) { + return (EnumDescriptor)result; + } else { + return null; + } + } + + /** + * Find a service type in the file by name. + * + * @param name The unqualified type name to look for. + * @return The service type's descriptor, or {@code null} if not found. + */ + public ServiceDescriptor findServiceByName(String name) { + // Don't allow looking up nested types. This will make optimization + // easier later. + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof ServiceDescriptor && + result.getFile() == this) { + return (ServiceDescriptor)result; + } else { + return null; + } + } + + /** + * Find an extension in the file by name. Does not find extensions nested + * inside message types. + * + * @param name The unqualified extension name to look for. + * @return The extension's descriptor, or {@code null} if not found. + */ + public FieldDescriptor findExtensionByName(String name) { + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof FieldDescriptor && + result.getFile() == this) { + return (FieldDescriptor)result; + } else { + return null; + } + } + + /** + * Construct a {@code FileDescriptor}. + * + * @param proto The protocol message form of the FileDescriptor. + * @param dependencies {@code FileDescriptor}s corresponding to all of + * the file's dependencies, in the exact order listed + * in {@code proto}. + * @throws DescriptorValidationException {@code proto} is not a valid + * descriptor. This can occur for a number of reasons, e.g. + * because a field has an undefined type or because two messages + * were defined with the same name. + */ + public static FileDescriptor buildFrom(FileDescriptorProto proto, + FileDescriptor[] dependencies) + throws DescriptorValidationException { + // Building decsriptors involves two steps: translating and linking. + // In the translation step (implemented by FileDescriptor's + // constructor), we build an object tree mirroring the + // FileDescriptorProto's tree and put all of the descriptors into the + // DescriptorPool's lookup tables. In the linking step, we look up all + // type references in the DescriptorPool, so that, for example, a + // FieldDescriptor for an embedded message contains a pointer directly + // to the Descriptor for that message's type. We also detect undefined + // types in the linking step. + DescriptorPool pool = new DescriptorPool(dependencies); + FileDescriptor result = new FileDescriptor(proto, dependencies, pool); + + if (dependencies.length != proto.getDependencyCount()) { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.buildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + for (int i = 0; i < proto.getDependencyCount(); i++) { + if (!dependencies[i].getName().equals(proto.getDependency(i))) { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.buildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + } + + result.crossLink(); + return result; + } + + /** + * This method is to be called by generated code only. It is equivalent + * to {@code buildFrom} except that the {@code FileDescriptorProto} is + * encoded in protocol buffer wire format. + */ + public static FileDescriptor internalBuildGeneratedFileFrom( + String descriptorData, FileDescriptor[] dependencies) + throws DescriptorValidationException, + InvalidProtocolBufferException { + // Hack: We can't embed a raw byte array inside generated Java code + // (at least, not efficiently), but we can embed Strings. So, the + // protocol compiler embeds the FileDescriptorProto as a giant + // string literal which is passed to this function to construct the + // file's FileDescriptor. The string literal contains only 8-bit + // characters, each one representing a byte of the FileDescriptorProto's + // serialized form. So, if we convert it to bytes in ISO-8859-1, we + // should get the original bytes that we want. + try { + FileDescriptorProto proto = + FileDescriptorProto.parseFrom(descriptorData.getBytes("ISO-8859-1")); + return buildFrom(proto, dependencies); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException( + "Standard encoding ISO-8859-1 not supported by JVM.", e); + } + } + + private final FileDescriptorProto proto; + private final Descriptor[] messageTypes; + private final EnumDescriptor[] enumTypes; + private final ServiceDescriptor[] services; + private final FieldDescriptor[] extensions; + private final FileDescriptor[] dependencies; + private final DescriptorPool pool; + + private FileDescriptor(FileDescriptorProto proto, + FileDescriptor[] dependencies, + DescriptorPool pool) + throws DescriptorValidationException { + this.pool = pool; + this.proto = proto; + this.dependencies = dependencies.clone(); + + pool.addPackage(getPackage(), this); + + messageTypes = new Descriptor[proto.getMessageTypeCount()]; + for (int i = 0; i < proto.getMessageTypeCount(); i++) { + messageTypes[i] = + new Descriptor(proto.getMessageType(i), this, null, i); + } + + enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; + for (int i = 0; i < proto.getEnumTypeCount(); i++) { + enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i); + } + + services = new ServiceDescriptor[proto.getServiceCount()]; + for (int i = 0; i < proto.getServiceCount(); i++) { + services[i] = new ServiceDescriptor(proto.getService(i), this, i); + } + + extensions = new FieldDescriptor[proto.getExtensionCount()]; + for (int i = 0; i < proto.getExtensionCount(); i++) { + extensions[i] = new FieldDescriptor( + proto.getExtension(i), this, null, i, true); + } + } + + /** Look up and cross-link all field types, etc. */ + private void crossLink() throws DescriptorValidationException { + for (int i = 0; i < messageTypes.length; i++) { + messageTypes[i].crossLink(); + } + + for (int i = 0; i < services.length; i++) { + services[i].crossLink(); + } + + for (int i = 0; i < extensions.length; i++) { + extensions[i].crossLink(); + } + } + } + + // ================================================================= + + /** Describes a message type. */ + public static final class Descriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. In other words, + * given a {@link FileDescriptor} {@code file}, the following is true: + *

+     *   for all i in [0, file.getMessageTypeCount()):
+     *     file.getMessageType(i).getIndex() == i
+     * 
+ * Similarly, for a {@link Descriptor} {@code messageType}: + *
+     *   for all i in [0, messageType.getNestedTypeCount()):
+     *     messageType.getNestedType(i).getIndex() == i
+     * 
+ */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public DescriptorProto toProto() { return proto; } + + /** Get the type's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the type's fully-qualified name, within the proto language's + * namespace. This differs from the Java name. For example, given this + * {@code .proto}: + *
+     *   package foo.bar;
+     *   option java_package = "com.example.protos"
+     *   message Baz {}
+     * 
+ * {@code Baz}'s full name is "foo.bar.Baz". + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** If this is a nested type, get the outer descriptor, otherwise null. */ + public Descriptor getContainingType() { return containingType; } + + /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */ + public MessageOptions getOptions() { return proto.getOptions(); } + + /** Get a list of this message type's fields. */ + public List getFields() { + return Collections.unmodifiableList(Arrays.asList(fields)); + } + + /** Get a list of this message type's extensions. */ + public List getExtensions() { + return Collections.unmodifiableList(Arrays.asList(extensions)); + } + + /** Get a list of message types nested within this one. */ + public List getNestedTypes() { + return Collections.unmodifiableList(Arrays.asList(nestedTypes)); + } + + /** Get a list of enum types nested within this one. */ + public List getEnumTypes() { + return Collections.unmodifiableList(Arrays.asList(enumTypes)); + } + + /** Determines if the given field number is an extension. */ + public boolean isExtensionNumber(int number) { + for (DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) { + if (range.getStart() <= number && number < range.getEnd()) { + return true; + } + } + return false; + } + + /** + * Finds a field by name. + * @param name The unqualified name of the field (e.g. "foo"). + * @return The field's descriptor, or {@code null} if not found. + */ + public FieldDescriptor findFieldByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof FieldDescriptor) { + return (FieldDescriptor)result; + } else { + return null; + } + } + + /** + * Finds a field by field number. + * @param number The field number within this message type. + * @return The field's descriptor, or {@code null} if not found. + */ + public FieldDescriptor findFieldByNumber(int number) { + return file.pool.fieldsByNumber.get( + new DescriptorPool.DescriptorIntPair(this, number)); + } + + /** + * Finds a nested message type by name. + * @param name The unqualified name of the nested type (e.g. "Foo"). + * @return The types's descriptor, or {@code null} if not found. + */ + public Descriptor findNestedTypeByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof Descriptor) { + return (Descriptor)result; + } else { + return null; + } + } + + /** + * Finds a nested enum type by name. + * @param name The unqualified name of the nested type (e.g. "Foo"). + * @return The types's descriptor, or {@code null} if not found. + */ + public EnumDescriptor findEnumTypeByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof EnumDescriptor) { + return (EnumDescriptor)result; + } else { + return null; + } + } + + private final int index; + private final DescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final Descriptor containingType; + private final Descriptor[] nestedTypes; + private final EnumDescriptor[] enumTypes; + private final FieldDescriptor[] fields; + private final FieldDescriptor[] extensions; + + private Descriptor(DescriptorProto proto, + FileDescriptor file, + Descriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, parent, proto.getName()); + this.file = file; + this.containingType = parent; + + this.nestedTypes = new Descriptor[proto.getNestedTypeCount()]; + for (int i = 0; i < proto.getNestedTypeCount(); i++) { + this.nestedTypes[i] = new Descriptor( + proto.getNestedType(i), file, this, i); + } + + this.enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; + for (int i = 0; i < proto.getEnumTypeCount(); i++) { + this.enumTypes[i] = new EnumDescriptor( + proto.getEnumType(i), file, this, i); + } + + this.fields = new FieldDescriptor[proto.getFieldCount()]; + for (int i = 0; i < proto.getFieldCount(); i++) { + this.fields[i] = new FieldDescriptor( + proto.getField(i), file, this, i, false); + } + + this.extensions = new FieldDescriptor[proto.getExtensionCount()]; + for (int i = 0; i < proto.getExtensionCount(); i++) { + this.extensions[i] = new FieldDescriptor( + proto.getExtension(i), file, this, i, true); + } + + file.pool.addSymbol(this); + } + + /** Look up and cross-link all field types, etc. */ + private void crossLink() throws DescriptorValidationException { + for (int i = 0; i < nestedTypes.length; i++) { + nestedTypes[i].crossLink(); + } + + for (int i = 0; i < fields.length; i++) { + fields[i].crossLink(); + } + + for (int i = 0; i < extensions.length; i++) { + extensions[i].crossLink(); + } + } + } + + // ================================================================= + + /** Describes a field of a message type. */ + public static final class FieldDescriptor + implements GenericDescriptor, Comparable { + /** + * Get the index of this descriptor within its parent. + * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public FieldDescriptorProto toProto() { return proto; } + + /** Get the field's unqualified name. */ + public String getName() { return proto.getName(); } + + /** Get the field's number. */ + public int getNumber() { return proto.getNumber(); } + + /** + * Get the field's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** + * Get the field's java type. This is just for convenience. Every + * {@code FieldDescriptorProto.Type} maps to exactly one Java type. + */ + public JavaType getJavaType() { return type.getJavaType(); } + + /** Get the {@code FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the field's declared type. */ + public Type getType() { return type; } + + /** Is this field declared required? */ + public boolean isRequired() { + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED; + } + + /** Is this field declared optional? */ + public boolean isOptional() { + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL; + } + + /** Is this field declared repeated? */ + public boolean isRepeated() { + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; + } + + /** Returns true if the field had an explicitly-defined default value. */ + public boolean hasDefaultValue() { return proto.hasDefaultValue(); } + + /** + * Returns the field's default value. Valid for all types except for + * messages and groups. For all other types, the object returned is of + * the same class that would returned by Message.getField(this). + */ + public Object getDefaultValue() { + if (getJavaType() == JavaType.MESSAGE) { + throw new UnsupportedOperationException( + "FieldDescriptor.getDefaultValue() called on an embedded message " + + "field."); + } + return defaultValue; + } + + /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */ + public FieldOptions getOptions() { return proto.getOptions(); } + + /** Is this field an extension? */ + public boolean isExtension() { return proto.hasExtendee(); } + + /** + * Get the field's containing type. For extensions, this is the type being + * extended, not the location where the extension was defined. See + * {@link #getExtensionScope()}. + */ + public Descriptor getContainingType() { return containingType; } + + /** + * For extensions defined nested within message types, gets the outer + * type. Not valid for non-extension fields. For example, consider + * this {@code .proto} file: + *
+     *   message Foo {
+     *     extensions 1000 to max;
+     *   }
+     *   extend Foo {
+     *     optional int32 baz = 1234;
+     *   }
+     *   message Bar {
+     *     extend Foo {
+     *       optional int32 qux = 4321;
+     *     }
+     *   }
+     * 
+ * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. + * However, {@code baz}'s extension scope is {@code null} while + * {@code qux}'s extension scope is {@code Bar}. + */ + public Descriptor getExtensionScope() { + if (!isExtension()) { + throw new UnsupportedOperationException( + "This field is not an extension."); + } + return extensionScope; + } + + /** For embedded message and group fields, gets the field's type. */ + public Descriptor getMessageType() { + if (getJavaType() != JavaType.MESSAGE) { + throw new UnsupportedOperationException( + "This field is not of message type."); + } + return messageType; + } + + /** For enum fields, gets the field's type. */ + public EnumDescriptor getEnumType() { + if (getJavaType() != JavaType.ENUM) { + throw new UnsupportedOperationException( + "This field is not of enum type."); + } + return enumType; + } + + /** + * Compare with another {@code FieldDescriptor}. This orders fields in + * "canonical" order, which simply means ascending order by field number. + * {@code other} must be a field of the same type -- i.e. + * {@code getContainingType()} must return the same {@code Descriptor} for + * both fields. + * + * @return negative, zero, or positive if {@code this} is less than, + * equal to, or greater than {@code other}, respectively. + */ + public int compareTo(FieldDescriptor other) { + if (other.containingType != containingType) { + throw new IllegalArgumentException( + "FieldDescriptors can only be compared to other FieldDescriptors " + + "for fields of the same message type."); + } + return getNumber() - other.getNumber(); + } + + private final int index; + + private final FieldDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final Descriptor extensionScope; + + // Possibly initialized during cross-linking. + private Type type; + private Descriptor containingType; + private Descriptor messageType; + private EnumDescriptor enumType; + private Object defaultValue; + + public static enum Type { + DOUBLE (FieldDescriptorProto.Type.TYPE_DOUBLE , JavaType.DOUBLE ), + FLOAT (FieldDescriptorProto.Type.TYPE_FLOAT , JavaType.FLOAT ), + INT64 (FieldDescriptorProto.Type.TYPE_INT64 , JavaType.LONG ), + UINT64 (FieldDescriptorProto.Type.TYPE_UINT64 , JavaType.LONG ), + INT32 (FieldDescriptorProto.Type.TYPE_INT32 , JavaType.INT ), + FIXED64 (FieldDescriptorProto.Type.TYPE_FIXED64 , JavaType.LONG ), + FIXED32 (FieldDescriptorProto.Type.TYPE_FIXED32 , JavaType.INT ), + BOOL (FieldDescriptorProto.Type.TYPE_BOOL , JavaType.BOOLEAN ), + STRING (FieldDescriptorProto.Type.TYPE_STRING , JavaType.STRING ), + GROUP (FieldDescriptorProto.Type.TYPE_GROUP , JavaType.MESSAGE ), + MESSAGE (FieldDescriptorProto.Type.TYPE_MESSAGE , JavaType.MESSAGE ), + BYTES (FieldDescriptorProto.Type.TYPE_BYTES , JavaType.BYTE_STRING), + UINT32 (FieldDescriptorProto.Type.TYPE_UINT32 , JavaType.INT ), + ENUM (FieldDescriptorProto.Type.TYPE_ENUM , JavaType.ENUM ), + SFIXED32(FieldDescriptorProto.Type.TYPE_SFIXED32, JavaType.INT ), + SFIXED64(FieldDescriptorProto.Type.TYPE_SFIXED64, JavaType.LONG ), + SINT32 (FieldDescriptorProto.Type.TYPE_SINT32 , JavaType.INT ), + SINT64 (FieldDescriptorProto.Type.TYPE_SINT64 , JavaType.LONG ); + + private Type(FieldDescriptorProto.Type proto, JavaType javaType) { + this.proto = proto; + this.javaType = javaType; + + if (this.ordinal() != proto.getNumber() - 1) { + throw new RuntimeException( + "descriptor.proto changed but Desrciptors.java wasn't updated."); + } + } + + private FieldDescriptorProto.Type proto; + private JavaType javaType; + + public FieldDescriptorProto.Type toProto() { return proto; } + public JavaType getJavaType() { return javaType; } + + public static Type valueOf(FieldDescriptorProto.Type type) { + return values()[type.getNumber() - 1]; + } + } + + static { + // Refuse to init if someone added a new declared type. + if (Type.values().length != FieldDescriptorProto.Type.values().length) { + throw new RuntimeException( + "descriptor.proto has a new declared type but Desrciptors.java " + + "wasn't updated."); + } + } + + public static enum JavaType { + INT(0), + LONG(0L), + FLOAT(0F), + DOUBLE(0D), + BOOLEAN(false), + STRING(""), + BYTE_STRING(ByteString.EMPTY), + ENUM(null), + MESSAGE(null); + + private JavaType(Object defaultDefault) { + this.defaultDefault = defaultDefault; + } + + /** + * The default default value for fields of this type, if it's a primitive + * type. This is meant for use inside this file only, hence is private. + */ + private Object defaultDefault; + } + + private FieldDescriptor(FieldDescriptorProto proto, + FileDescriptor file, + Descriptor parent, + int index, + boolean isExtension) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, parent, proto.getName()); + this.file = file; + + if (proto.hasType()) { + this.type = Type.valueOf(proto.getType()); + } + + if (getNumber() <= 0) { + throw new DescriptorValidationException(this, + "Field numbers must be positive integers."); + } + + if (isExtension) { + if (!proto.hasExtendee()) { + throw new DescriptorValidationException(this, + "FieldDescriptorProto.extendee not set for extension field."); + } + this.containingType = null; // Will be filled in when cross-linking + if (parent != null) { + this.extensionScope = parent; + } else { + this.extensionScope = null; + } + } else { + if (proto.hasExtendee()) { + throw new DescriptorValidationException(this, + "FieldDescriptorProto.extendee set for non-extension field."); + } + this.containingType = parent; + this.extensionScope = null; + } + + file.pool.addSymbol(this); + } + + /** Look up and cross-link all field types, etc. */ + private void crossLink() throws DescriptorValidationException { + if (proto.hasExtendee()) { + GenericDescriptor extendee = + file.pool.lookupSymbol(proto.getExtendee(), this); + if (!(extendee instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getExtendee() + "\" is not a message type."); + } + this.containingType = (Descriptor)extendee; + + if (!getContainingType().isExtensionNumber(getNumber())) { + throw new DescriptorValidationException(this, + "\"" + getContainingType().getFullName() + "\" does not declare " + + getNumber() + " as an extension number."); + } + } + + if (proto.hasTypeName()) { + GenericDescriptor typeDescriptor = + file.pool.lookupSymbol(proto.getTypeName(), this); + + if (!proto.hasType()) { + // Choose field type based on symbol. + if (typeDescriptor instanceof Descriptor) { + this.type = Type.MESSAGE; + } else if (typeDescriptor instanceof EnumDescriptor) { + this.type = Type.ENUM; + } else { + throw new DescriptorValidationException(this, + "\"" + proto.getTypeName() + "\" is not a type."); + } + } + + if (getJavaType() == JavaType.MESSAGE) { + if (!(typeDescriptor instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getTypeName() + "\" is not a message type."); + } + this.messageType = (Descriptor)typeDescriptor; + + if (proto.hasDefaultValue()) { + throw new DescriptorValidationException(this, + "Messages can't have default values."); + } + } else if (getJavaType() == JavaType.ENUM) { + if (!(typeDescriptor instanceof EnumDescriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getTypeName() + "\" is not an enum type."); + } + this.enumType = (EnumDescriptor)typeDescriptor; + } else { + throw new DescriptorValidationException(this, + "Field with primitive type has type_name."); + } + } else { + if (getJavaType() == JavaType.MESSAGE || + getJavaType() == JavaType.ENUM) { + throw new DescriptorValidationException(this, + "Field with message or enum type missing type_name."); + } + } + + // We don't attempt to parse the default value until here because for + // enums we need the enum type's descriptor. + if (proto.hasDefaultValue()) { + if (isRepeated()) { + throw new DescriptorValidationException(this, + "Repeated fields cannot have default values."); + } + + try { + switch (getType()) { + case INT32: + case SINT32: + case SFIXED32: + defaultValue = TextFormat.parseInt32(proto.getDefaultValue()); + break; + case UINT32: + case FIXED32: { + defaultValue = TextFormat.parseUInt32(proto.getDefaultValue()); + break; + } + case INT64: + case SINT64: + case SFIXED64: + defaultValue = TextFormat.parseInt64(proto.getDefaultValue()); + break; + case UINT64: + case FIXED64: { + defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); + break; + } + case FLOAT: + defaultValue = Float.valueOf(proto.getDefaultValue()); + break; + case DOUBLE: + defaultValue = Double.valueOf(proto.getDefaultValue()); + break; + case BOOL: + defaultValue = Boolean.valueOf(proto.getDefaultValue()); + break; + case STRING: + defaultValue = proto.getDefaultValue(); + break; + case BYTES: + try { + defaultValue = + TextFormat.unescapeBytes(proto.getDefaultValue()); + } catch (TextFormat.InvalidEscapeSequence e) { + throw new DescriptorValidationException(this, + "Couldn't parse default value: " + e.getMessage()); + } + break; + case ENUM: + defaultValue = enumType.findValueByName(proto.getDefaultValue()); + if (defaultValue == null) { + throw new DescriptorValidationException(this, + "Unknown enum default value: \"" + + proto.getDefaultValue() + "\""); + } + break; + case MESSAGE: + case GROUP: + throw new DescriptorValidationException(this, + "Message type had default value."); + } + } catch (NumberFormatException e) { + DescriptorValidationException validationException = + new DescriptorValidationException(this, + "Could not parse default value: \"" + + proto.getDefaultValue() + "\""); + validationException.initCause(e); + throw validationException; + } + } else { + // Determine the default default for this field. + if (isRepeated()) { + defaultValue = Collections.EMPTY_LIST; + } else { + switch (getJavaType()) { + case ENUM: + // We guarantee elsewhere that an enum type always has at least + // one possible value. + defaultValue = enumType.getValues().get(0); + break; + case MESSAGE: + defaultValue = null; + break; + default: + defaultValue = getJavaType().defaultDefault; + break; + } + } + } + + if (!isExtension()) { + file.pool.addFieldByNumber(this); + } + + if (containingType != null && + containingType.getOptions().getMessageSetWireFormat()) { + if (isExtension()) { + if (!isOptional() || getType() != Type.MESSAGE) { + throw new DescriptorValidationException(this, + "Extensions of MessageSets must be optional messages."); + } + } else { + throw new DescriptorValidationException(this, + "MessageSets cannot have fields, only extensions."); + } + } + } + } + + // ================================================================= + + /** Describes an enum type. */ + public static final class EnumDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public EnumDescriptorProto toProto() { return proto; } + + /** Get the type's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the type's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** If this is a nested type, get the outer descriptor, otherwise null. */ + public Descriptor getContainingType() { return containingType; } + + /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */ + public EnumOptions getOptions() { return proto.getOptions(); } + + /** Get a list of defined values for this enum. */ + public List getValues() { + return Collections.unmodifiableList(Arrays.asList(values)); + } + + /** + * Find an enum value by name. + * @param name The unqualified name of the value (e.g. "FOO"). + * @return the value's decsriptor, or {@code null} if not found. + */ + public EnumValueDescriptor findValueByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof EnumValueDescriptor) { + return (EnumValueDescriptor)result; + } else { + return null; + } + } + + /** + * Find an enum value by number. If multiple enum values have the same + * number, this returns the first defined value with that number. + * @param number The value's number. + * @return the value's decsriptor, or {@code null} if not found. + */ + public EnumValueDescriptor findValueByNumber(int number) { + return file.pool.enumValuesByNumber.get( + new DescriptorPool.DescriptorIntPair(this, number)); + } + + private final int index; + private final EnumDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final Descriptor containingType; + private EnumValueDescriptor[] values; + + private EnumDescriptor(EnumDescriptorProto proto, + FileDescriptor file, + Descriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, parent, proto.getName()); + this.file = file; + this.containingType = parent; + + if (proto.getValueCount() == 0) { + // We cannot allow enums with no values because this would mean there + // would be no valid default value for fields of this type. + throw new DescriptorValidationException(this, + "Enums must contain at least one value."); + } + + values = new EnumValueDescriptor[proto.getValueCount()]; + for (int i = 0; i < proto.getValueCount(); i++) { + this.values[i] = new EnumValueDescriptor( + proto.getValue(i), file, this, i); + } + + file.pool.addSymbol(this); + } + } + + // ================================================================= + + /** + * Describes one value within an enum type. Note that multiple defined + * values may have the same number. In generated Java code, all values + * with the same number after the first become aliases of the first. + * However, they still have independent EnumValueDescriptors. + */ + public static final class EnumValueDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public EnumValueDescriptorProto toProto() { return proto; } + + /** Get the value's unqualified name. */ + public String getName() { return proto.getName(); } + + /** Get the value's number. */ + public int getNumber() { return proto.getNumber(); } + + /** + * Get the value's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the value's enum type. */ + public EnumDescriptor getType() { return type; } + + /** + * Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. + */ + public EnumValueOptions getOptions() { return proto.getOptions(); } + + private final int index; + private final EnumValueDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final EnumDescriptor type; + + private EnumValueDescriptor(EnumValueDescriptorProto proto, + FileDescriptor file, + EnumDescriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.file = file; + this.type = parent; + + this.fullName = parent.getFullName() + "." + proto.getName(); + + file.pool.addSymbol(this); + file.pool.addEnumValueByNumber(this); + } + } + + // ================================================================= + + /** Describes a service type. */ + public static final class ServiceDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public ServiceDescriptorProto toProto() { return proto; } + + /** Get the type's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the type's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ + public ServiceOptions getOptions() { return proto.getOptions(); } + + /** Get a list of methods for this service. */ + public List getMethods() { + return Collections.unmodifiableList(Arrays.asList(methods)); + } + + /** + * Find a method by name. + * @param name The unqualified name of the method (e.g. "Foo"). + * @return the method's decsriptor, or {@code null} if not found. + */ + public MethodDescriptor findMethodByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof MethodDescriptor) { + return (MethodDescriptor)result; + } else { + return null; + } + } + + private final int index; + private final ServiceDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private MethodDescriptor[] methods; + + private ServiceDescriptor(ServiceDescriptorProto proto, + FileDescriptor file, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, null, proto.getName()); + this.file = file; + + this.methods = new MethodDescriptor[proto.getMethodCount()]; + for (int i = 0; i < proto.getMethodCount(); i++) { + this.methods[i] = new MethodDescriptor( + proto.getMethod(i), file, this, i); + } + + file.pool.addSymbol(this); + } + + private void crossLink() throws DescriptorValidationException { + for (int i = 0; i < methods.length; i++) { + methods[i].crossLink(); + } + } + } + + // ================================================================= + + /** + * Describes one method within a service type. + */ + public static final class MethodDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public MethodDescriptorProto toProto() { return proto; } + + /** Get the method's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the method's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the method's service type. */ + public ServiceDescriptor getService() { return service; } + + /** Get the method's input type. */ + public Descriptor getInputType() { return inputType; } + + /** Get the method's output type. */ + public Descriptor getOutputType() { return outputType; } + + /** + * Get the {@code MethodOptions}, defined in {@code descriptor.proto}. + */ + public MethodOptions getOptions() { return proto.getOptions(); } + + private final int index; + private final MethodDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final ServiceDescriptor service; + + // Initialized during cross-linking. + private Descriptor inputType; + private Descriptor outputType; + + private MethodDescriptor(MethodDescriptorProto proto, + FileDescriptor file, + ServiceDescriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.file = file; + this.service = parent; + + this.fullName = parent.getFullName() + "." + proto.getName(); + + file.pool.addSymbol(this); + } + + private void crossLink() throws DescriptorValidationException { + GenericDescriptor inputType = + file.pool.lookupSymbol(proto.getInputType(), this); + if (!(inputType instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getInputType() + "\" is not a message type."); + } + this.inputType = (Descriptor)inputType; + + GenericDescriptor outputType = + file.pool.lookupSymbol(proto.getOutputType(), this); + if (!(outputType instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getOutputType() + "\" is not a message type."); + } + this.outputType = (Descriptor)outputType; + } + } + + // ================================================================= + + private static String computeFullName(FileDescriptor file, + Descriptor parent, + String name) { + if (parent != null) { + return parent.getFullName() + "." + name; + } else if (file.getPackage().length() > 0) { + return file.getPackage() + "." + name; + } else { + return name; + } + } + + // ================================================================= + + /** + * All descriptors except {@code FileDescriptor} implement this to make + * {@code DescriptorPool}'s life easier. + */ + private static interface GenericDescriptor { + Message toProto(); + String getName(); + String getFullName(); + FileDescriptor getFile(); + } + + /** + * Thrown when building descriptors fails because the source DescriptorProtos + * are not valid. + */ + public static class DescriptorValidationException extends Exception { + /** Gets the full name of the descriptor where the error occurred. */ + public String getProblemSymbolName() { return name; } + + /** + * Gets the the protocol message representation of the invalid descriptor. + */ + public Message getProblemProto() { return proto; } + + /** + * Gets a human-readable description of the error. + */ + public String getDescription() { return description; } + + private final String name; + private final Message proto; + private final String description; + + private DescriptorValidationException(GenericDescriptor problemDescriptor, + String description) { + super(problemDescriptor.getFullName() + ": " + description); + + // Note that problemDescriptor may be partially uninitialized, so we + // don't want to expose it directly to the user. So, we only provide + // the name and the original proto. + this.name = problemDescriptor.getFullName(); + this.proto = problemDescriptor.toProto(); + this.description = description; + } + + private DescriptorValidationException(FileDescriptor problemDescriptor, + String description) { + super(problemDescriptor.getName() + ": " + description); + + // Note that problemDescriptor may be partially uninitialized, so we + // don't want to expose it directly to the user. So, we only provide + // the name and the original proto. + this.name = problemDescriptor.getName(); + this.proto = problemDescriptor.toProto(); + this.description = description; + } + } + + // ================================================================= + + /** + * A private helper class which contains lookup tables containing all the + * descriptors defined in a particular file. + */ + private static final class DescriptorPool { + DescriptorPool(FileDescriptor[] dependencies) { + this.dependencies = new DescriptorPool[dependencies.length]; + + for (int i = 0; i < dependencies.length; i++) { + this.dependencies[i] = dependencies[i].pool; + } + + for (int i = 0; i < dependencies.length; i++) { + try { + addPackage(dependencies[i].getPackage(), dependencies[i]); + } catch (DescriptorValidationException e) { + // Can't happen, because addPackage() only fails when the name + // conflicts with a non-package, but we have not yet added any + // non-packages at this point. + assert false; + } + } + } + + final DescriptorPool[] dependencies; + + final Map descriptorsByName = + new HashMap(); + final Map fieldsByNumber = + new HashMap(); + final Map enumValuesByNumber = + new HashMap(); + + /** Find a generic descriptor by fully-qualified name. */ + GenericDescriptor findSymbol(String fullName) { + GenericDescriptor result = descriptorsByName.get(fullName); + if (result != null) return result; + + for (int i = 0; i < dependencies.length; i++) { + result = dependencies[i].descriptorsByName.get(fullName); + if (result != null) return result; + } + + return null; + } + + /** + * Look up a descriptor by name, relative to some other descriptor. + * The name may be fully-qualified (with a leading '.'), + * partially-qualified, or unqualified. C++-like name lookup semantics + * are used to search for the matching descriptor. + */ + GenericDescriptor lookupSymbol(String name, + GenericDescriptor relativeTo) + throws DescriptorValidationException { + // TODO(kenton): This could be optimized in a number of ways. + + GenericDescriptor result; + if (name.startsWith(".")) { + // Fully-qualified name. + result = findSymbol(name.substring(1)); + } else { + // If "name" is a compound identifier, we want to search for the + // first component of it, then search within it for the rest. + int firstPartLength = name.indexOf('.'); + String firstPart; + if (firstPartLength == -1) { + firstPart = name; + } else { + firstPart = name.substring(0, firstPartLength); + } + + // We will search each parent scope of "relativeTo" looking for the + // symbol. + StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName()); + + while (true) { + // Chop off the last component of the scope. + int dotpos = scopeToTry.lastIndexOf("."); + if (dotpos == -1) { + result = findSymbol(name); + break; + } else { + scopeToTry.setLength(dotpos + 1); + + // Append firstPart and try to find. + scopeToTry.append(firstPart); + result = findSymbol(scopeToTry.toString()); + + if (result != null) { + if (firstPartLength != -1) { + // We only found the first part of the symbol. Now look for + // the whole thing. If this fails, we *don't* want to keep + // searching parent scopes. + scopeToTry.setLength(dotpos + 1); + scopeToTry.append(name); + result = findSymbol(scopeToTry.toString()); + } + break; + } + + // Not found. Remove the name so we can try again. + scopeToTry.setLength(dotpos); + } + } + } + + if (result == null) { + throw new DescriptorValidationException(relativeTo, + "\"" + name + "\" is not defined."); + } else { + return result; + } + } + + /** + * Adds a symbol to the symbol table. If a symbol with the same name + * already exists, throws an error. + */ + void addSymbol(GenericDescriptor descriptor) + throws DescriptorValidationException { + validateSymbolName(descriptor); + + String fullName = descriptor.getFullName(); + int dotpos = fullName.lastIndexOf('.'); + + GenericDescriptor old = descriptorsByName.put(fullName, descriptor); + if (old != null) { + descriptorsByName.put(fullName, old); + + if (descriptor.getFile() == old.getFile()) { + if (dotpos == -1) { + throw new DescriptorValidationException(descriptor, + "\"" + fullName + "\" is already defined."); + } else { + throw new DescriptorValidationException(descriptor, + "\"" + fullName.substring(dotpos + 1) + + "\" is already defined in \"" + + fullName.substring(0, dotpos) + "\"."); + } + } else { + throw new DescriptorValidationException(descriptor, + "\"" + fullName + "\" is already defined in file \"" + + old.getFile().getName() + "\"."); + } + } + } + + /** + * Represents a package in the symbol table. We use PackageDescriptors + * just as placeholders so that someone cannot define, say, a message type + * that has the same name as an existing package. + */ + static final class PackageDescriptor implements GenericDescriptor { + public Message toProto() { return file.toProto(); } + public String getName() { return name; } + public String getFullName() { return fullName; } + public FileDescriptor getFile() { return file; } + + PackageDescriptor(String name, String fullName, FileDescriptor file) { + this.file = file; + this.fullName = fullName; + this.name = name; + } + + String name; + String fullName; + FileDescriptor file; + } + + /** + * Adds a package to the symbol tables. If a package by the same name + * already exists, that is fine, but if some other kind of symbol exists + * under the same name, an exception is thrown. If the package has + * multiple components, this also adds the parent package(s). + */ + void addPackage(String fullName, FileDescriptor file) + throws DescriptorValidationException { + int dotpos = fullName.lastIndexOf('.'); + String name; + if (dotpos != -1) { + addPackage(fullName.substring(0, dotpos), file); + name = fullName.substring(dotpos + 1); + } else { + name = fullName; + } + + GenericDescriptor old = + descriptorsByName.put(fullName, + new PackageDescriptor(fullName, name, file)); + if (old != null) { + descriptorsByName.put(fullName, old); + if (!(old instanceof PackageDescriptor)) { + throw new DescriptorValidationException(file, + "\"" + name + "\" is already defined (as something other than a " + + "package) in file \"" + old.getFile().getName() + "\"."); + } + } + } + + /** A (GenericDescriptor, int) pair, used as a map key. */ + static final class DescriptorIntPair { + final GenericDescriptor descriptor; + final int number; + + DescriptorIntPair(GenericDescriptor descriptor, int number) { + this.descriptor = descriptor; + this.number = number; + } + + public int hashCode() { + return descriptor.hashCode() * ((1 << 16) - 1) + number; + } + public boolean equals(Object obj) { + if (!(obj instanceof DescriptorIntPair)) return false; + DescriptorIntPair other = (DescriptorIntPair)obj; + return descriptor == other.descriptor && number == other.number; + } + } + + /** + * Adds a field to the fieldsByNumber table. Throws an exception if a + * field with hte same containing type and number already exists. + */ + void addFieldByNumber(FieldDescriptor field) + throws DescriptorValidationException { + DescriptorIntPair key = + new DescriptorIntPair(field.getContainingType(), field.getNumber()); + FieldDescriptor old = fieldsByNumber.put(key, field); + if (old != null) { + fieldsByNumber.put(key, old); + throw new DescriptorValidationException(field, + "Field number " + field.getNumber() + + "has already been used in \"" + + field.getContainingType().getFullName() + + "\" by field \"" + old.getName() + "\"."); + } + } + + /** + * Adds an enum value to the enumValuesByNumber table. If an enum value + * with the same type and number already exists, does nothing. (This is + * allowed; the first value define with the number takes precedence.) + */ + void addEnumValueByNumber(EnumValueDescriptor value) { + DescriptorIntPair key = + new DescriptorIntPair(value.getType(), value.getNumber()); + EnumValueDescriptor old = enumValuesByNumber.put(key, value); + if (old != null) { + enumValuesByNumber.put(key, old); + // Not an error: Multiple enum values may have the same number, but + // we only want the first one in the map. + } + } + + /** + * Verifies that the descriptor's name is valid (i.e. it contains only + * letters, digits, and underscores, and does not start with a digit). + */ + void validateSymbolName(GenericDescriptor descriptor) + throws DescriptorValidationException { + String name = descriptor.getName(); + if (name.length() == 0) { + throw new DescriptorValidationException(descriptor, "Missing name."); + } else { + boolean valid = true; + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + // Non-ASCII characters are not valid in protobuf identifiers, even + // if they are letters or digits. + if (c >= 128) { + valid = false; + } + // First character must be letter or _. Subsequent characters may + // be letters, numbers, or digits. + if (Character.isLetter(c) || c == '_' || + (Character.isDigit(c) && i > 0)) { + // Valid + } else { + valid = false; + } + } + if (!valid) { + throw new DescriptorValidationException(descriptor, + "\"" + name + "\" is not a valid identifier."); + } + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java new file mode 100644 index 00000000..d9a39f0e --- /dev/null +++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java @@ -0,0 +1,391 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.io.InputStream; +import java.io.IOException; +import java.util.Map; + +/** + * An implementation of {@link Message} that can represent arbitrary types, + * given a {@link Descriptors.Descriptor}. + * + * @author kenton@google.com Kenton Varda + */ +public final class DynamicMessage extends AbstractMessage { + private final Descriptor type; + private final FieldSet fields; + private final UnknownFieldSet unknownFields; + private int memoizedSize = -1; + + /** + * Construct a {@code DynamicMessage} using the given {@code FieldSet}. + */ + private DynamicMessage(Descriptor type, FieldSet fields, + UnknownFieldSet unknownFields) { + this.type = type; + this.fields = fields; + this.unknownFields = unknownFields; + } + + /** + * Get a {@code DynamicMessage} representing the default instance of the + * given type. + */ + public static DynamicMessage getDefaultInstance(Descriptor type) { + return new DynamicMessage(type, FieldSet.emptySet(), + UnknownFieldSet.getDefaultInstance()); + } + + /** Parse a message of the given type from the given input stream. */ + public static DynamicMessage parseFrom(Descriptor type, + CodedInputStream input) + throws IOException { + return newBuilder(type).mergeFrom(input).buildParsed(); + } + + /** Parse a message of the given type from the given input stream. */ + public static DynamicMessage parseFrom( + Descriptor type, + CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, ByteString data) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, ByteString data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, byte[] data) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, byte[] data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed(); + } + + /** Parse a message of the given type from {@code input} and return it. */ + public static DynamicMessage parseFrom(Descriptor type, InputStream input) + throws IOException { + return newBuilder(type).mergeFrom(input).buildParsed(); + } + + /** Parse a message of the given type from {@code input} and return it. */ + public static DynamicMessage parseFrom(Descriptor type, InputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed(); + } + + /** Construct a {@link Message.Builder} for the given type. */ + public static Builder newBuilder(Descriptor type) { + return new Builder(type); + } + + /** + * Construct a {@link Message.Builder} for a message of the same type as + * {@code prototype}, and initialize it with {@code prototype}'s contents. + */ + public static Builder newBuilder(Message prototype) { + return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype); + } + + // ----------------------------------------------------------------- + // Implementation of Message interface. + + public Descriptor getDescriptorForType() { + return type; + } + + public DynamicMessage getDefaultInstanceForType() { + return getDefaultInstance(type); + } + + public Map getAllFields() { + return fields.getAllFields(); + } + + public boolean hasField(FieldDescriptor field) { + verifyContainingType(field); + return fields.hasField(field); + } + + public Object getField(FieldDescriptor field) { + verifyContainingType(field); + Object result = fields.getField(field); + if (result == null) { + result = getDefaultInstance(field.getMessageType()); + } + return result; + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + verifyContainingType(field); + return fields.getRepeatedFieldCount(field); + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + verifyContainingType(field); + return fields.getRepeatedField(field, index); + } + + public UnknownFieldSet getUnknownFields() { + return unknownFields; + } + + public boolean isInitialized() { + return fields.isInitialized(type); + } + + public void writeTo(CodedOutputStream output) throws IOException { + fields.writeTo(output); + if (type.getOptions().getMessageSetWireFormat()) { + unknownFields.writeAsMessageSetTo(output); + } else { + unknownFields.writeTo(output); + } + } + + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = fields.getSerializedSize(); + if (type.getOptions().getMessageSetWireFormat()) { + size += unknownFields.getSerializedSizeAsMessageSet(); + } else { + size += unknownFields.getSerializedSize(); + } + + memoizedSize = size; + return size; + } + + public Builder newBuilderForType() { + return new Builder(type); + } + + /** Verifies that the field is a field of this message. */ + private void verifyContainingType(FieldDescriptor field) { + if (field.getContainingType() != type) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } + } + + // ================================================================= + + /** + * Builder for {@link DynamicMessage}s. + */ + public static final class Builder extends AbstractMessage.Builder { + private final Descriptor type; + private FieldSet fields; + private UnknownFieldSet unknownFields; + + /** Construct a {@code Builder} for the given type. */ + private Builder(Descriptor type) { + this.type = type; + this.fields = FieldSet.newFieldSet(); + this.unknownFields = UnknownFieldSet.getDefaultInstance(); + } + + // --------------------------------------------------------------- + // Implementation of Message.Builder interface. + + public Builder clear() { + fields.clear(); + return this; + } + + public Builder mergeFrom(Message other) { + if (other.getDescriptorForType() != type) { + throw new IllegalArgumentException( + "mergeFrom(Message) can only merge messages of the same type."); + } + + fields.mergeFrom(other); + return this; + } + + public DynamicMessage build() { + if (!isInitialized()) { + throw new UninitializedMessageException( + new DynamicMessage(type, fields, unknownFields)); + } + return buildPartial(); + } + + /** + * Helper for DynamicMessage.parseFrom() methods to call. Throws + * {@link InvalidProtocolBufferException} instead of + * {@link UninitializedMessageException}. + */ + private DynamicMessage buildParsed() throws InvalidProtocolBufferException { + if (!isInitialized()) { + throw new UninitializedMessageException( + new DynamicMessage(type, fields, unknownFields)) + .asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public DynamicMessage buildPartial() { + fields.makeImmutable(); + DynamicMessage result = + new DynamicMessage(type, fields, unknownFields); + fields = null; + unknownFields = null; + return result; + } + + public Builder clone() { + Builder result = new Builder(type); + result.fields.mergeFrom(fields); + return result; + } + + public boolean isInitialized() { + return fields.isInitialized(type); + } + + public Builder mergeFrom(CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + UnknownFieldSet.Builder unknownFieldsBuilder = + UnknownFieldSet.newBuilder(unknownFields); + fields.mergeFrom(input, unknownFieldsBuilder, extensionRegistry, this); + unknownFields = unknownFieldsBuilder.build(); + return this; + } + + public Descriptor getDescriptorForType() { + return type; + } + + public DynamicMessage getDefaultInstanceForType() { + return getDefaultInstance(type); + } + + public Map getAllFields() { + return fields.getAllFields(); + } + + public Builder newBuilderForField(FieldDescriptor field) { + verifyContainingType(field); + + if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalArgumentException( + "newBuilderForField is only valid for fields with message type."); + } + + return new Builder(field.getMessageType()); + } + + public boolean hasField(FieldDescriptor field) { + verifyContainingType(field); + return fields.hasField(field); + } + + public Object getField(FieldDescriptor field) { + verifyContainingType(field); + Object result = fields.getField(field); + if (result == null) { + result = getDefaultInstance(field.getMessageType()); + } + return result; + } + + public Builder setField(FieldDescriptor field, Object value) { + verifyContainingType(field); + fields.setField(field, value); + return this; + } + + public Builder clearField(FieldDescriptor field) { + verifyContainingType(field); + fields.clearField(field); + return this; + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + verifyContainingType(field); + return fields.getRepeatedFieldCount(field); + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + verifyContainingType(field); + return fields.getRepeatedField(field, index); + } + + public Builder setRepeatedField(FieldDescriptor field, + int index, Object value) { + verifyContainingType(field); + fields.setRepeatedField(field, index, value); + return this; + } + + public Builder addRepeatedField(FieldDescriptor field, Object value) { + verifyContainingType(field); + fields.addRepeatedField(field, value); + return this; + } + + public UnknownFieldSet getUnknownFields() { + return unknownFields; + } + + public Builder setUnknownFields(UnknownFieldSet unknownFields) { + this.unknownFields = unknownFields; + return this; + } + + public Builder mergeUnknownFields(UnknownFieldSet unknownFields) { + this.unknownFields = + UnknownFieldSet.newBuilder(this.unknownFields) + .mergeFrom(unknownFields) + .build(); + return this; + } + + /** Verifies that the field is a field of this message. */ + private void verifyContainingType(FieldDescriptor field) { + if (field.getContainingType() != type) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java new file mode 100644 index 00000000..6954b3b6 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java @@ -0,0 +1,237 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * A table of known extensions, searchable by name or field number. When + * parsing a protocol message that might have extensions, you must provide + * an {@code ExtensionRegistry} in which you have registered any extensions + * that you want to be able to parse. Otherwise, those extensions will just + * be treated like unknown fields. + * + *

For example, if you had the {@code .proto} file: + * + *

+ * option java_class = "MyProto";
+ *
+ * message Foo {
+ *   extensions 1000 to max;
+ * }
+ *
+ * extend Foo {
+ *   optional int32 bar;
+ * }
+ * 
+ * + * Then you might write code like: + * + *
+ * ExtensionRegistry registry = ExtensionRegistry.newInstance();
+ * registry.add(MyProto.bar);
+ * MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
+ * 
+ * + *

Background: + * + *

You might wonder why this is necessary. Two alternatives might come to + * mind. First, you might imagine a system where generated extensions are + * automatically registered when their containing classes are loaded. This + * is a popular technique, but is bad design; among other things, it creates a + * situation where behavior can change depending on what classes happen to be + * loaded. It also introduces a security vulnerability, because an + * unprivileged class could cause its code to be called unexpectedly from a + * privileged class by registering itself as an extension of the right type. + * + *

Another option you might consider is lazy parsing: do not parse an + * extension until it is first requested, at which point the caller must + * provide a type to use. This introduces a different set of problems. First, + * it would require a mutex lock any time an extension was accessed, which + * would be slow. Second, corrupt data would not be detected until first + * access, at which point it would be much harder to deal with it. Third, it + * could violate the expectation that message objects are immutable, since the + * type provided could be any arbitrary message class. An unpriviledged user + * could take advantage of this to inject a mutable object into a message + * belonging to priviledged code and create mischief. + * + * @author kenton@google.com Kenton Varda + */ +public final class ExtensionRegistry { + /** Construct a new, empty instance. */ + public static ExtensionRegistry newInstance() { + return new ExtensionRegistry( + new HashMap(), + new HashMap()); + } + + /** Get the unmodifiable singleton empty instance. */ + public static ExtensionRegistry getEmptyRegistry() { + return EMPTY; + } + + /** Returns an unmodifiable view of the registry. */ + public ExtensionRegistry getUnmodifiable() { + return new ExtensionRegistry( + Collections.unmodifiableMap(extensionsByName), + Collections.unmodifiableMap(extensionsByNumber)); + } + + /** A (Descriptor, Message) pair, returned by lookup methods. */ + public static final class ExtensionInfo { + /** The extension's descriptor. */ + public final FieldDescriptor descriptor; + + /** + * A default instance of the extension's type, if it has a message type. + * Otherwise, {@code null}. + */ + public final Message defaultInstance; + + private ExtensionInfo(FieldDescriptor descriptor) { + this.descriptor = descriptor; + this.defaultInstance = null; + } + private ExtensionInfo(FieldDescriptor descriptor, Message defaultInstance) { + this.descriptor = descriptor; + this.defaultInstance = defaultInstance; + } + } + + /** + * Find an extension by fully-qualified field name, in the proto namespace. + * I.e. {@code result.descriptor.fullName()} will match {@code fullName} if + * a match is found. + * + * @return Information about the extension if found, or {@code null} + * otherwise. + */ + public ExtensionInfo findExtensionByName(String fullName) { + return extensionsByName.get(fullName); + } + + /** + * Find an extension by containing type and field number. + * + * @return Information about the extension if found, or {@code null} + * otherwise. + */ + public ExtensionInfo findExtensionByNumber(Descriptor containingType, + int fieldNumber) { + return extensionsByNumber.get( + new DescriptorIntPair(containingType, fieldNumber)); + } + + /** Add an extension from a generated file to the registry. */ + public void add(GeneratedMessage.GeneratedExtension extension) { + if (extension.getDescriptor().getJavaType() == + FieldDescriptor.JavaType.MESSAGE) { + add(new ExtensionInfo(extension.getDescriptor(), + extension.getMessageDefaultInstance())); + } else { + add(new ExtensionInfo(extension.getDescriptor(), null)); + } + } + + /** Add a non-message-type extension to the registry by descriptor. */ + public void add(FieldDescriptor type) { + if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalArgumentException( + "ExtensionRegistry.add() must be provided a default instance when " + + "adding an embedded message extension."); + } + add(new ExtensionInfo(type, null)); + } + + /** Add a message-type extension to the registry by descriptor. */ + public void add(FieldDescriptor type, Message defaultInstance) { + if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalArgumentException( + "ExtensionRegistry.add() provided a default instance for a " + + "non-message extension."); + } + add(new ExtensionInfo(type, defaultInstance)); + } + + // ================================================================= + // Private stuff. + + private ExtensionRegistry( + Map extensionsByName, + Map extensionsByNumber) { + this.extensionsByName = extensionsByName; + this.extensionsByNumber = extensionsByNumber; + } + + private final Map extensionsByName; + private final Map extensionsByNumber; + + private static final ExtensionRegistry EMPTY = + new ExtensionRegistry( + Collections.emptyMap(), + Collections.emptyMap()); + + private void add(ExtensionInfo extension) { + if (!extension.descriptor.isExtension()) { + throw new IllegalArgumentException( + "ExtensionRegistry.add() was given a FieldDescriptor for a regular " + + "(non-extension) field."); + } + + extensionsByName.put(extension.descriptor.getFullName(), extension); + extensionsByNumber.put( + new DescriptorIntPair(extension.descriptor.getContainingType(), + extension.descriptor.getNumber()), + extension); + + FieldDescriptor field = extension.descriptor; + if (field.getContainingType().getOptions().getMessageSetWireFormat() && + field.getType() == FieldDescriptor.Type.MESSAGE && + field.isOptional() && + field.getExtensionScope() == field.getMessageType()) { + // This is an extension of a MessageSet type defined within the extension + // type's own scope. For backwards-compatibility, allow it to be looked + // up by type name. + extensionsByName.put(field.getMessageType().getFullName(), extension); + } + } + + /** A (GenericDescriptor, int) pair, used as a map key. */ + private static final class DescriptorIntPair { + final Descriptor descriptor; + final int number; + + DescriptorIntPair(Descriptor descriptor, int number) { + this.descriptor = descriptor; + this.number = number; + } + + public int hashCode() { + return descriptor.hashCode() * ((1 << 16) - 1) + number; + } + public boolean equals(Object obj) { + if (!(obj instanceof DescriptorIntPair)) return false; + DescriptorIntPair other = (DescriptorIntPair)obj; + return descriptor == other.descriptor && number == other.number; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java new file mode 100644 index 00000000..3a5dc488 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/FieldSet.java @@ -0,0 +1,662 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.TreeMap; +import java.util.List; +import java.util.Map; + +/** + * A class which represents an arbitrary set of fields of some message type. + * This is used to implement {@link DynamicMessage}, and also to represent + * extensions in {@link GeneratedMessage}. This class is package-private, + * since outside users should probably be using {@link DynamicMessage}. + * + * @author kenton@google.com Kenton Varda + */ +final class FieldSet { + private Map fields; + + /** Construct a new FieldSet. */ + private FieldSet() { + // Use a TreeMap because fields need to be in canonical order when + // serializing. + this.fields = new TreeMap(); + } + + /** + * Construct a new FieldSet with the given map. This is only used by + * DEFAULT_INSTANCE, to pass in an immutable empty map. + */ + private FieldSet(Map fields) { + this.fields = fields; + } + + /** Construct a new FieldSet. */ + public static FieldSet newFieldSet() { + return new FieldSet(); + } + + /** Get an immutable empty FieldSet. */ + public static FieldSet emptySet() { + return DEFAULT_INSTANCE; + } + private static final FieldSet DEFAULT_INSTANCE = + new FieldSet(Collections.emptyMap()); + + /** Make this FieldSet immutable from this point forward. */ + @SuppressWarnings("unchecked") + public void makeImmutable() { + for (Map.Entry entry: fields.entrySet()) { + if (entry.getKey().isRepeated()) { + List value = (List)entry.getValue(); + entry.setValue(Collections.unmodifiableList(value)); + } + } + fields = Collections.unmodifiableMap(fields); + } + + // ================================================================= + + /** See {@link Message.Builder#clear()}. */ + public void clear() { + fields.clear(); + } + + /** See {@link Message#getAllFields()}. */ + public Map getAllFields() { + return Collections.unmodifiableMap(fields); + } + + /** + * Get an interator to the field map. This iterator should not be leaked + * out of the protobuf library as it is not protected from mutation. + */ + public Iterator> iterator() { + return fields.entrySet().iterator(); + } + + /** See {@link Message#hasField(Descriptors.FieldDescriptor)}. */ + public boolean hasField(Descriptors.FieldDescriptor field) { + if (field.isRepeated()) { + throw new IllegalArgumentException( + "hasField() can only be called on non-repeated fields."); + } + + return fields.containsKey(field); + } + + /** + * See {@link Message#getField(Descriptors.FieldDescriptor)}. This method + * returns {@code null} if the field is a singular message type and is not + * set; in this case it is up to the caller to fetch the message's default + * instance. + */ + public Object getField(Descriptors.FieldDescriptor field) { + Object result = fields.get(field); + if (result == null) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + return Collections.emptyList(); + } else { + return null; + } + } else { + return field.getDefaultValue(); + } + } else { + return result; + } + } + + /** See {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. */ + @SuppressWarnings("unchecked") + public void setField(Descriptors.FieldDescriptor field, Object value) { + if (field.isRepeated()) { + if (!(value instanceof List)) { + throw new IllegalArgumentException( + "Wrong object type used with protocol message reflection."); + } + + // Wrap the contents in a new list so that the caller cannot change + // the list's contents after setting it. + List newList = new ArrayList(); + newList.addAll((List)value); + for (Object element : newList) { + verifyType(field, element); + } + value = newList; + } else { + verifyType(field, value); + } + + fields.put(field, value); + } + + /** See {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */ + public void clearField(Descriptors.FieldDescriptor field) { + fields.remove(field); + } + + /** See {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */ + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "getRepeatedFieldCount() can only be called on repeated fields."); + } + + return ((List)getField(field)).size(); + } + + /** See {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. */ + public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "getRepeatedField() can only be called on repeated fields."); + } + + return ((List)getField(field)).get(index); + } + + /** See {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. */ + @SuppressWarnings("unchecked") + public void setRepeatedField(Descriptors.FieldDescriptor field, int index, + Object value) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "setRepeatedField() can only be called on repeated fields."); + } + + verifyType(field, value); + + List list = (List)fields.get(field); + if (list == null) { + throw new IndexOutOfBoundsException(); + } + + list.set(index, value); + } + + /** See {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. */ + @SuppressWarnings("unchecked") + public void addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "setRepeatedField() can only be called on repeated fields."); + } + + verifyType(field, value); + + List list = (List)fields.get(field); + if (list == null) { + list = new ArrayList(); + fields.put(field, list); + } + + list.add(value); + } + + /** + * Verifies that the given object is of the correct type to be a valid + * value for the given field. (For repeated fields, this checks if the + * object is the right type to be one element of the field.) + * + * @throws IllegalArgumentException The value is not of the right type. + */ + private void verifyType(FieldDescriptor field, Object value) { + boolean isValid = false; + switch (field.getJavaType()) { + case INT: isValid = value instanceof Integer ; break; + case LONG: isValid = value instanceof Long ; break; + case FLOAT: isValid = value instanceof Float ; break; + case DOUBLE: isValid = value instanceof Double ; break; + case BOOLEAN: isValid = value instanceof Boolean ; break; + case STRING: isValid = value instanceof String ; break; + case BYTE_STRING: isValid = value instanceof ByteString; break; + case ENUM: + isValid = value instanceof EnumValueDescriptor && + ((EnumValueDescriptor)value).getType() == field.getEnumType(); + break; + case MESSAGE: + isValid = value instanceof Message && + ((Message)value).getDescriptorForType() == field.getMessageType(); + break; + } + + if (!isValid) { + // When chaining calls to setField(), it can be hard to tell from + // the stack trace which exact call failed, since the whole chain is + // considered one line of code. So, let's make sure to include the + // field name and other useful info in the exception. + throw new IllegalArgumentException( + "Wrong object type used with protocol message reflection. " + + "Message type \"" + field.getContainingType().getFullName() + + "\", field \"" + + (field.isExtension() ? field.getFullName() : field.getName()) + + "\", value was type \"" + value.getClass().getName() + "\"."); + } + } + + // ================================================================= + // Parsing and serialization + + /** + * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} + * itself does not have any way of knowing about required fields that + * aren't actually present in the set, it is up to the caller to check + * that all required fields are present. + */ + @SuppressWarnings("unchecked") + public boolean isInitialized() { + for (Map.Entry entry : fields.entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + for (Message element : (List) entry.getValue()) { + if (!element.isInitialized()) { + return false; + } + } + } else { + if (!((Message) entry.getValue()).isInitialized()) { + return false; + } + } + } + } + + return true; + } + + /** + * Like {@link #isInitialized()}, but also checks for the presence of + * all required fields in the given type. + */ + @SuppressWarnings("unchecked") + public boolean isInitialized(Descriptor type) { + // Check that all required fields are present. + for (FieldDescriptor field : type.getFields()) { + if (field.isRequired()) { + if (!hasField(field)) { + return false; + } + } + } + + // Check that embedded messages are initialized. + return isInitialized(); + } + + /** See {@link Message.Builder#mergeFrom(Message)}. */ + @SuppressWarnings("unchecked") + public void mergeFrom(Message other) { + // Note: We don't attempt to verify that other's fields have valid + // types. Doing so would be a losing battle. We'd have to verify + // all sub-messages as well, and we'd have to make copies of all of + // them to insure that they don't change after verification (since + // the Message interface itself cannot enforce immutability of + // implementations). + // TODO(kenton): Provide a function somewhere called makeDeepCopy() + // which allows people to make secure deep copies of messages. + + for (Map.Entry entry : + other.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + List existingValue = (List)fields.get(field); + if (existingValue == null) { + existingValue = new ArrayList(); + fields.put(field, existingValue); + } + existingValue.addAll((List)entry.getValue()); + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + Message existingValue = (Message)fields.get(field); + if (existingValue == null) { + setField(field, entry.getValue()); + } else { + setField(field, + existingValue.newBuilderForType() + .mergeFrom(existingValue) + .mergeFrom((Message)entry.getValue()) + .build()); + } + } else { + setField(field, entry.getValue()); + } + } + } + + /** + * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}. + */ + @SuppressWarnings("unchecked") + public void mergeFrom(FieldSet other) { + for (Map.Entry entry : other.fields.entrySet()) { + FieldDescriptor field = entry.getKey(); + Object value = entry.getValue(); + + if (field.isRepeated()) { + List existingValue = (List)fields.get(field); + if (existingValue == null) { + existingValue = new ArrayList(); + fields.put(field, existingValue); + } + existingValue.addAll((List)value); + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + Message existingValue = (Message)fields.get(field); + if (existingValue == null) { + setField(field, value); + } else { + setField(field, + existingValue.newBuilderForType() + .mergeFrom(existingValue) + .mergeFrom((Message)value) + .build()); + } + } else { + setField(field, value); + } + } + } + + // TODO(kenton): Move parsing code into AbstractMessage, since it no longer + // uses any special knowledge from FieldSet. + + /** + * See {@link Message.Builder#mergeFrom(CodedInputStream)}. + * @param builder The {@code Builder} for the target message. + */ + public static void mergeFrom(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws java.io.IOException { + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + if (!mergeFieldFrom(input, unknownFields, extensionRegistry, + builder, tag)) { + // end group tag + break; + } + } + } + + /** + * Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder, + * ExtensionRegistry, Message.Builder)}, but parses a single field. + * @param tag The tag, which should have already been read. + * @return {@code true} unless the tag is an end-group tag. + */ + @SuppressWarnings("unchecked") + public static boolean mergeFieldFrom( + CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + Message.Builder builder, + int tag) throws java.io.IOException { + Descriptor type = builder.getDescriptorForType(); + + if (type.getOptions().getMessageSetWireFormat() && + tag == WireFormat.MESSAGE_SET_ITEM_TAG) { + mergeMessageSetExtensionFromCodedStream( + input, unknownFields, extensionRegistry, builder); + return true; + } + + int wireType = WireFormat.getTagWireType(tag); + int fieldNumber = WireFormat.getTagFieldNumber(tag); + + FieldDescriptor field; + Message defaultInstance = null; + + if (type.isExtensionNumber(fieldNumber)) { + ExtensionRegistry.ExtensionInfo extension = + extensionRegistry.findExtensionByNumber(type, fieldNumber); + if (extension == null) { + field = null; + } else { + field = extension.descriptor; + defaultInstance = extension.defaultInstance; + } + } else { + field = type.findFieldByNumber(fieldNumber); + } + + if (field == null || + wireType != WireFormat.getWireFormatForFieldType(field.getType())) { + // Unknown field or wrong wire type. Skip. + return unknownFields.mergeFieldFrom(tag, input); + } else { + Object value; + switch (field.getType()) { + case GROUP: { + Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readGroup(field.getNumber(), subBuilder, extensionRegistry); + value = subBuilder.build(); + break; + } + case MESSAGE: { + Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readMessage(subBuilder, extensionRegistry); + value = subBuilder.build(); + break; + } + case ENUM: { + int rawValue = input.readEnum(); + value = field.getEnumType().findValueByNumber(rawValue); + // If the number isn't recognized as a valid value for this enum, + // drop it. + if (value == null) { + unknownFields.mergeVarintField(fieldNumber, rawValue); + return true; + } + break; + } + default: + value = input.readPrimitiveField(field.getType()); + break; + } + + if (field.isRepeated()) { + builder.addRepeatedField(field, value); + } else { + builder.setField(field, value); + } + } + + return true; + } + + /** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */ + private static void mergeMessageSetExtensionFromCodedStream( + CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + Message.Builder builder) throws java.io.IOException { + Descriptor type = builder.getDescriptorForType(); + + // The wire format for MessageSet is: + // message MessageSet { + // repeated group Item = 1 { + // required int32 typeId = 2; + // required bytes message = 3; + // } + // } + // "typeId" is the extension's field number. The extension can only be + // a message type, where "message" contains the encoded bytes of that + // message. + // + // In practice, we will probably never see a MessageSet item in which + // the message appears before the type ID, or where either field does not + // appear exactly once. However, in theory such cases are valid, so we + // should be prepared to accept them. + + int typeId = 0; + ByteString rawBytes = null; // If we encounter "message" before "typeId" + Message.Builder subBuilder = null; + FieldDescriptor field = null; + + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { + typeId = input.readUInt32(); + // Zero is not a valid type ID. + if (typeId != 0) { + ExtensionRegistry.ExtensionInfo extension = + extensionRegistry.findExtensionByNumber(type, typeId); + if (extension != null) { + field = extension.descriptor; + subBuilder = extension.defaultInstance.newBuilderForType(); + Message originalMessage = (Message)builder.getField(field); + if (originalMessage != null) { + subBuilder.mergeFrom(originalMessage); + } + if (rawBytes != null) { + // We already encountered the message. Parse it now. + subBuilder.mergeFrom( + CodedInputStream.newInstance(rawBytes.newInput())); + rawBytes = null; + } + } else { + // Unknown extension number. If we already saw data, put it + // in rawBytes. + if (rawBytes != null) { + unknownFields.mergeField(typeId, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(rawBytes) + .build()); + rawBytes = null; + } + } + } + } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { + if (typeId == 0) { + // We haven't seen a type ID yet, so we have to store the raw bytes + // for now. + rawBytes = input.readBytes(); + } else if (subBuilder == null) { + // We don't know how to parse this. Ignore it. + unknownFields.mergeField(typeId, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(input.readBytes()) + .build()); + } else { + // We already know the type, so we can parse directly from the input + // with no copying. Hooray! + input.readMessage(subBuilder, extensionRegistry); + } + } else { + // Unknown tag. Skip it. + if (!input.skipField(tag)) { + break; // end of group + } + } + } + + input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG); + + if (subBuilder != null) { + builder.setField(field, subBuilder.build()); + } + } + + /** See {@link Message#writeTo(CodedOutputStream)}. */ + public void writeTo(CodedOutputStream output) + throws java.io.IOException { + for (Map.Entry entry : fields.entrySet()) { + writeField(entry.getKey(), entry.getValue(), output); + } + } + + /** Write a single field. */ + public void writeField(FieldDescriptor field, Object value, + CodedOutputStream output) throws java.io.IOException { + if (field.isExtension() && + field.getContainingType().getOptions().getMessageSetWireFormat()) { + output.writeMessageSetExtension(field.getNumber(), (Message)value); + } else { + if (field.isRepeated()) { + for (Object element : (List)value) { + output.writeField(field.getType(), field.getNumber(), element); + } + } else { + output.writeField(field.getType(), field.getNumber(), value); + } + } + } + + /** + * See {@link Message#getSerializedSize()}. It's up to the caller to cache + * the resulting size if desired. + */ + public int getSerializedSize() { + int size = 0; + for (Map.Entry entry : fields.entrySet()) { + FieldDescriptor field = entry.getKey(); + Object value = entry.getValue(); + + if (field.isExtension() && + field.getContainingType().getOptions().getMessageSetWireFormat()) { + size += CodedOutputStream.computeMessageSetExtensionSize( + field.getNumber(), (Message)value); + } else { + if (field.isRepeated()) { + for (Object element : (List)value) { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), element); + } + } else { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), value); + } + } + } + return size; + } +} diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java new file mode 100644 index 00000000..957965b7 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -0,0 +1,1219 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * All generated protocol message classes extend this class. This class + * implements most of the Message and Builder interfaces using Java reflection. + * Users can ignore this class and pretend that generated messages implement + * the Message interface directly. + * + * @author kenton@google.com Kenton Varda + */ +public abstract class GeneratedMessage extends AbstractMessage { + protected GeneratedMessage() {} + + private UnknownFieldSet unknownFields = UnknownFieldSet.getDefaultInstance(); + + /** + * Get the FieldAccessorTable for this type. We can't have the message + * class pass this in to the constructor because of bootstrapping trouble + * with DescriptorProtos. + */ + protected abstract FieldAccessorTable internalGetFieldAccessorTable(); + + public Descriptor getDescriptorForType() { + return internalGetFieldAccessorTable().descriptor; + } + + /** Internal helper which returns a mutable map. */ + private final Map getAllFieldsMutable() { + TreeMap result = + new TreeMap(); + Descriptor descriptor = internalGetFieldAccessorTable().descriptor; + for (FieldDescriptor field : descriptor.getFields()) { + if (field.isRepeated()) { + List value = (List)getField(field); + if (!value.isEmpty()) { + result.put(field, value); + } + } else { + if (hasField(field)) { + result.put(field, getField(field)); + } + } + } + return result; + } + + public Map getAllFields() { + return Collections.unmodifiableMap(getAllFieldsMutable()); + } + + public boolean hasField(Descriptors.FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).has(this); + } + + public Object getField(FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).get(this); + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field) + .getRepeatedCount(this); + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + return internalGetFieldAccessorTable().getField(field) + .getRepeated(this, index); + } + + public final UnknownFieldSet getUnknownFields() { + return unknownFields; + } + + @SuppressWarnings("unchecked") + public abstract static class Builder + extends AbstractMessage.Builder { + protected Builder() {} + + /** + * Get the message being built. We don't just pass this to the + * constructor because it becomes null when build() is called. + */ + protected abstract GeneratedMessage internalGetResult(); + + /** + * Get the FieldAccessorTable for this type. We can't have the message + * class pass this in to the constructor because of bootstrapping trouble + * with DescriptorProtos. + */ + private FieldAccessorTable internalGetFieldAccessorTable() { + return internalGetResult().internalGetFieldAccessorTable(); + } + + public BuilderType mergeFrom(Message other) { + if (other.getDescriptorForType() != + internalGetFieldAccessorTable().descriptor) { + throw new IllegalArgumentException("Message type mismatch."); + } + + for (Map.Entry entry : + other.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + // Concatenate repeated fields. + for (Object element : (List) entry.getValue()) { + addRepeatedField(field, element); + } + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE && + hasField(field)) { + // Merge singular embedded messages. + Message oldValue = (Message) getField(field); + setField(field, + oldValue.newBuilderForType() + .mergeFrom(oldValue) + .mergeFrom((Message) entry.getValue()) + .buildPartial()); + } else { + // Just overwrite. + setField(field, entry.getValue()); + } + } + return (BuilderType)this; + } + + public Descriptor getDescriptorForType() { + return internalGetFieldAccessorTable().descriptor; + } + + public Map getAllFields() { + return internalGetResult().getAllFields(); + } + + public Message.Builder newBuilderForField( + Descriptors.FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).newBuilder(); + } + + public boolean hasField(Descriptors.FieldDescriptor field) { + return internalGetResult().hasField(field); + } + + public Object getField(Descriptors.FieldDescriptor field) { + if (field.isRepeated()) { + // The underlying list object is still modifiable at this point. + // Make sure not to expose the modifiable list to the caller. + return Collections.unmodifiableList( + (List)internalGetResult().getField(field)); + } else { + return internalGetResult().getField(field); + } + } + + public BuilderType setField(Descriptors.FieldDescriptor field, + Object value) { + internalGetFieldAccessorTable().getField(field).set(this, value); + return (BuilderType)this; + } + + public BuilderType clearField(Descriptors.FieldDescriptor field) { + internalGetFieldAccessorTable().getField(field).clear(this); + return (BuilderType)this; + } + + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + return internalGetResult().getRepeatedFieldCount(field); + } + + public Object getRepeatedField(Descriptors.FieldDescriptor field, + int index) { + return internalGetResult().getRepeatedField(field, index); + } + + public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + internalGetFieldAccessorTable().getField(field) + .setRepeated(this, index, value); + return (BuilderType)this; + } + + public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + internalGetFieldAccessorTable().getField(field).addRepeated(this, value); + return (BuilderType)this; + } + + public final UnknownFieldSet getUnknownFields() { + return internalGetResult().unknownFields; + } + + public final BuilderType setUnknownFields(UnknownFieldSet unknownFields) { + internalGetResult().unknownFields = unknownFields; + return (BuilderType)this; + } + + public final BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { + GeneratedMessage result = internalGetResult(); + result.unknownFields = + UnknownFieldSet.newBuilder(result.unknownFields) + .mergeFrom(unknownFields) + .build(); + return (BuilderType)this; + } + + public boolean isInitialized() { + return internalGetResult().isInitialized(); + } + + /** + * Called by subclasses to parse an unknown field. + * @return {@code true} unless the tag is an end-group tag. + */ + protected boolean parseUnknownField(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + int tag) + throws IOException { + return unknownFields.mergeFieldFrom(tag, input); + } + + protected void addAll(Iterable values, Collection list) { + if (values instanceof Collection) { + @SuppressWarnings("unsafe") + Collection collection = (Collection) values; + list.addAll(collection); + } else { + for (T value : values) { + list.add(value); + } + } + } + } + + // ================================================================= + // Extensions-related stuff + + /** + * Generated message classes for message types that contain extension ranges + * subclass this. + * + *

This class implements type-safe accessors for extensions. They + * implement all the same operations that you can do with normal fields -- + * e.g. "has", "get", and "getCount" -- but for extensions. The extensions + * are identified using instances of the class {@link GeneratedExtension}; + * the protocol compiler generates a static instance of this class for every + * extension in its input. Through the magic of generics, all is made + * type-safe. + * + *

For example, imagine you have the {@code .proto} file: + * + *

+   * option java_class = "MyProto";
+   *
+   * message Foo {
+   *   extensions 1000 to max;
+   * }
+   *
+   * extend Foo {
+   *   optional int32 bar;
+   * }
+   * 
+ * + *

Then you might write code like: + * + *

+   * MyProto.Foo foo = getFoo();
+   * int i = foo.getExtension(MyProto.bar);
+   * 
+ * + *

See also {@link ExtendableBuilder}. + */ + public abstract static class ExtendableMessage< + MessageType extends ExtendableMessage> + extends GeneratedMessage { + protected ExtendableMessage() {} + private final FieldSet extensions = FieldSet.newFieldSet(); + + private final void verifyExtensionContainingType( + GeneratedExtension extension) { + if (extension.getDescriptor().getContainingType() != + getDescriptorForType()) { + // This can only happen if someone uses unchecked operations. + throw new IllegalArgumentException( + "Extension is for type \"" + + extension.getDescriptor().getContainingType().getFullName() + + "\" which does not match message type \"" + + getDescriptorForType().getFullName() + "\"."); + } + } + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + GeneratedExtension extension) { + verifyExtensionContainingType(extension); + return extensions.hasField(extension.getDescriptor()); + } + + /** Get the number of elements in a repeated extension. */ + public final int getExtensionCount( + GeneratedExtension> extension) { + verifyExtensionContainingType(extension); + return extensions.getRepeatedFieldCount(extension.getDescriptor()); + } + + /** Get the value of an extension. */ + @SuppressWarnings("unchecked") + public final Type getExtension( + GeneratedExtension extension) { + verifyExtensionContainingType(extension); + Object value = extensions.getField(extension.getDescriptor()); + if (value == null) { + return (Type)extension.getMessageDefaultInstance(); + } else { + return (Type)extension.fromReflectionType(value); + } + } + + /** Get one element of a repeated extension. */ + @SuppressWarnings("unchecked") + public final Type getExtension( + GeneratedExtension> extension, int index) { + verifyExtensionContainingType(extension); + return (Type)extension.singularFromReflectionType( + extensions.getRepeatedField(extension.getDescriptor(), index)); + } + + /** Called by subclasses to check if all extensions are initialized. */ + protected boolean extensionsAreInitialized() { + return extensions.isInitialized(); + } + + /** + * Used by subclasses to serialize extensions. Extension ranges may be + * interleaved with field numbers, but we must write them in canonical + * (sorted by field number) order. ExtensionWriter helps us write + * individual ranges of extensions at once. + */ + protected class ExtensionWriter { + // Imagine how much simpler this code would be if Java iterators had + // a way to get the next element without advancing the iterator. + + final Iterator> iter = + extensions.iterator(); + Map.Entry next = null; + + private ExtensionWriter() { + if (iter.hasNext()) { + next = iter.next(); + } + } + + public void writeUntil(int end, CodedOutputStream output) + throws IOException { + while (next != null && next.getKey().getNumber() < end) { + extensions.writeField(next.getKey(), next.getValue(), output); + if (iter.hasNext()) { + next = iter.next(); + } else { + next = null; + } + } + } + } + + protected ExtensionWriter newExtensionWriter() { + return new ExtensionWriter(); + } + + /** Called by subclasses to compute the size of extensions. */ + protected int extensionsSerializedSize() { + return extensions.getSerializedSize(); + } + + // --------------------------------------------------------------- + // Reflection + + public Map getAllFields() { + Map result = super.getAllFieldsMutable(); + result.putAll(extensions.getAllFields()); + return Collections.unmodifiableMap(result); + } + + public boolean hasField(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.hasField(field); + } else { + return super.hasField(field); + } + } + + public Object getField(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + Object value = extensions.getField(field); + if (value == null) { + // Lacking an ExtensionRegistry, we have no way to determine the + // extension's real type, so we return a DynamicMessage. + return DynamicMessage.getDefaultInstance(field.getMessageType()); + } else { + return value; + } + } else { + return super.getField(field); + } + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedFieldCount(field); + } else { + return super.getRepeatedFieldCount(field); + } + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedField(field, index); + } else { + return super.getRepeatedField(field, index); + } + } + + private void verifyContainingType(FieldDescriptor field) { + if (field.getContainingType() != getDescriptorForType()) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } + } + } + + /** + * Generated message builders for message types that contain extension ranges + * subclass this. + * + *

This class implements type-safe accessors for extensions. They + * implement all the same operations that you can do with normal fields -- + * e.g. "get", "set", and "add" -- but for extensions. The extensions are + * identified using instances of the class {@link GeneratedExtension}; the + * protocol compiler generates a static instance of this class for every + * extension in its input. Through the magic of generics, all is made + * type-safe. + * + *

For example, imagine you have the {@code .proto} file: + * + *

+   * option java_class = "MyProto";
+   *
+   * message Foo {
+   *   extensions 1000 to max;
+   * }
+   *
+   * extend Foo {
+   *   optional int32 bar;
+   * }
+   * 
+ * + *

Then you might write code like: + * + *

+   * MyProto.Foo foo =
+   *   MyProto.Foo.newBuilder()
+   *     .setExtension(MyProto.bar, 123)
+   *     .build();
+   * 
+ * + *

See also {@link ExtendableMessage}. + */ + @SuppressWarnings("unchecked") + public abstract static class ExtendableBuilder< + MessageType extends ExtendableMessage, + BuilderType extends ExtendableBuilder> + extends GeneratedMessage.Builder { + protected ExtendableBuilder() {} + protected abstract ExtendableMessage internalGetResult(); + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + GeneratedExtension extension) { + return internalGetResult().hasExtension(extension); + } + + /** Get the number of elements in a repeated extension. */ + public final int getExtensionCount( + GeneratedExtension> extension) { + return internalGetResult().getExtensionCount(extension); + } + + /** Get the value of an extension. */ + public final Type getExtension( + GeneratedExtension extension) { + return internalGetResult().getExtension(extension); + } + + /** Get one element of a repeated extension. */ + public final Type getExtension( + GeneratedExtension> extension, int index) { + return internalGetResult().getExtension(extension, index); + } + + /** Set the value of an extension. */ + public final BuilderType setExtension( + GeneratedExtension extension, Type value) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setField(extension.getDescriptor(), + extension.toReflectionType(value)); + return (BuilderType)this; + } + + /** Set the value of one element of a repeated extension. */ + public final BuilderType setExtension( + GeneratedExtension> extension, + int index, Type value) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setRepeatedField( + extension.getDescriptor(), index, + extension.singularToReflectionType(value)); + return (BuilderType)this; + } + + /** Append a value to a repeated extension. */ + public final BuilderType addExtension( + GeneratedExtension> extension, Type value) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.addRepeatedField( + extension.getDescriptor(), extension.singularToReflectionType(value)); + return (BuilderType)this; + } + + /** Clear an extension. */ + public final BuilderType clearExtension( + GeneratedExtension extension) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.clearField(extension.getDescriptor()); + return (BuilderType)this; + } + + /** + * Called by subclasses to parse an unknown field or an extension. + * @return {@code true} unless the tag is an end-group tag. + */ + protected boolean parseUnknownField(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + int tag) + throws IOException { + ExtendableMessage message = internalGetResult(); + return message.extensions.mergeFieldFrom( + input, unknownFields, extensionRegistry, this, tag); + } + + // --------------------------------------------------------------- + // Reflection + + // We don't have to override the get*() methods here because they already + // just forward to the underlying message. + + public BuilderType setField(FieldDescriptor field, Object value) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setField(field, value); + return (BuilderType)this; + } else { + return super.setField(field, value); + } + } + + public BuilderType clearField(Descriptors.FieldDescriptor field) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.clearField(field); + return (BuilderType)this; + } else { + return super.clearField(field); + } + } + + public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setRepeatedField(field, index, value); + return (BuilderType)this; + } else { + return super.setRepeatedField(field, index, value); + } + } + + public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.addRepeatedField(field, value); + return (BuilderType)this; + } else { + return super.addRepeatedField(field, value); + } + } + } + + // ----------------------------------------------------------------- + + /** For use by generated code only. */ + public static + GeneratedExtension + newGeneratedExtension(FieldDescriptor descriptor, Class type) { + if (descriptor.isRepeated()) { + throw new IllegalArgumentException( + "Must call newRepeatedGeneratedExtension() for repeated types."); + } + return new GeneratedExtension(descriptor, type); + } + + /** For use by generated code only. */ + public static + GeneratedExtension> + newRepeatedGeneratedExtension( + FieldDescriptor descriptor, Class type) { + if (!descriptor.isRepeated()) { + throw new IllegalArgumentException( + "Must call newGeneratedExtension() for non-repeated types."); + } + return new GeneratedExtension>(descriptor, type); + } + + /** + * Type used to represent generated extensions. The protocol compiler + * generates a static singleton instance of this class for each extension. + * + *

For example, imagine you have the {@code .proto} file: + * + *

+   * option java_class = "MyProto";
+   *
+   * message Foo {
+   *   extensions 1000 to max;
+   * }
+   *
+   * extend Foo {
+   *   optional int32 bar;
+   * }
+   * 
+ * + *

Then, {@code MyProto.Foo.bar} has type + * {@code GeneratedExtension}. + * + *

In general, users should ignore the details of this type, and simply use + * these static singletons as parameters to the extension accessors defined + * in {@link ExtendableMessage} and {@link ExtendableBuilder}. + */ + public static final class GeneratedExtension< + ContainingType extends Message, Type> { + // TODO(kenton): Find ways to avoid using Java reflection within this + // class. Also try to avoid suppressing unchecked warnings. + + private GeneratedExtension(FieldDescriptor descriptor, Class type) { + if (!descriptor.isExtension()) { + throw new IllegalArgumentException( + "GeneratedExtension given a regular (non-extension) field."); + } + + this.descriptor = descriptor; + this.type = type; + + switch (descriptor.getJavaType()) { + case MESSAGE: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = + (Message)invokeOrDie(getMethodOrDie(type, "getDefaultInstance"), + null); + break; + case ENUM: + enumValueOf = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + enumGetValueDescriptor = getMethodOrDie(type, "getValueDescriptor"); + messageDefaultInstance = null; + break; + default: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = null; + break; + } + } + + private final FieldDescriptor descriptor; + private final Class type; + private final Method enumValueOf; + private final Method enumGetValueDescriptor; + private final Message messageDefaultInstance; + + public FieldDescriptor getDescriptor() { return descriptor; } + + /** + * If the extension is an embedded message or group, returns the default + * instance of the message. + */ + @SuppressWarnings("unchecked") + public Message getMessageDefaultInstance() { + return messageDefaultInstance; + } + + /** + * Convert from the type used by the reflection accessors to the type used + * by native accessors. E.g., for enums, the reflection accessors use + * EnumValueDescriptors but the native accessors use the generated enum + * type. + */ + @SuppressWarnings("unchecked") + private Object fromReflectionType(Object value) { + if (descriptor.isRepeated()) { + if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE || + descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { + // Must convert the whole list. + List result = new ArrayList(); + for (Object element : (List)value) { + result.add(singularFromReflectionType(element)); + } + return result; + } else { + return value; + } + } else { + return singularFromReflectionType(value); + } + } + + /** + * Like {@link #fromReflectionType(Object)}, but if the type is a repeated + * type, this converts a single element. + */ + private Object singularFromReflectionType(Object value) { + switch (descriptor.getJavaType()) { + case MESSAGE: + if (type.isInstance(value)) { + return value; + } else { + // It seems the copy of the embedded message stored inside the + // extended message is not of the exact type the user was + // expecting. This can happen if a user defines a + // GeneratedExtension manually and gives it a different type. + // This should not happen in normal use. But, to be nice, we'll + // copy the message to whatever type the caller was expecting. + return messageDefaultInstance.newBuilderForType() + .mergeFrom((Message)value).build(); + } + case ENUM: + return invokeOrDie(enumValueOf, null, (EnumValueDescriptor)value); + default: + return value; + } + } + + /** + * Convert from the type used by the native accessors to the type used + * by reflection accessors. E.g., for enums, the reflection accessors use + * EnumValueDescriptors but the native accessors use the generated enum + * type. + */ + @SuppressWarnings("unchecked") + private Object toReflectionType(Object value) { + if (descriptor.isRepeated()) { + if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { + // Must convert the whole list. + List result = new ArrayList(); + for (Object element : (List)value) { + result.add(singularToReflectionType(element)); + } + return result; + } else { + return value; + } + } else { + return singularToReflectionType(value); + } + } + + /** + * Like {@link #toReflectionType(Object)}, but if the type is a repeated + * type, this converts a single element. + */ + private Object singularToReflectionType(Object value) { + switch (descriptor.getJavaType()) { + case ENUM: + return invokeOrDie(enumGetValueDescriptor, value); + default: + return value; + } + } + } + + // ================================================================= + + /** Calls Class.getMethod and throws a RuntimeException if it fails. */ + @SuppressWarnings("unchecked") + private static Method getMethodOrDie( + Class clazz, String name, Class... params) { + try { + return clazz.getMethod(name, params); + } catch (NoSuchMethodException e) { + throw new RuntimeException( + "Generated message class \"" + clazz.getName() + + "\" missing method \"" + name + "\".", e); + } + } + + /** Calls invoke and throws a RuntimeException if it fails. */ + private static Object invokeOrDie( + Method method, Object object, Object... params) { + try { + return method.invoke(object, params); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "Couldn't use Java reflection to implement protocol message " + + "reflection.", e); + } catch (java.lang.reflect.InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException)cause; + } else if (cause instanceof Error) { + throw (Error)cause; + } else { + throw new RuntimeException( + "Unexpected exception thrown by generated accessor method.", cause); + } + } + } + + /** + * Users should ignore this class. This class provides the implementation + * with access to the fields of a message object using Java reflection. + */ + public static final class FieldAccessorTable { + /** + * Construct a FieldAccessorTable for a particular message class. Only + * one FieldAccessorTable should ever be constructed per class. + * + * @param descriptor The type's descriptor. + * @param camelCaseNames The camelcase names of all fields in the message. + * These are used to derive the accessor method names. + * @param messageClass The message type. + * @param builderClass The builder type. + */ + public FieldAccessorTable( + Descriptor descriptor, + String[] camelCaseNames, + Class messageClass, + Class builderClass) { + this.descriptor = descriptor; + fields = new FieldAccessor[descriptor.getFields().size()]; + + for (int i = 0; i < fields.length; i++) { + FieldDescriptor field = descriptor.getFields().get(i); + if (field.isRepeated()) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + fields[i] = new RepeatedMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { + fields[i] = new RepeatedEnumFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else { + fields[i] = new RepeatedFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } + } else { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + fields[i] = new SingularMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { + fields[i] = new SingularEnumFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else { + fields[i] = new SingularFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } + } + } + } + + private final Descriptor descriptor; + private final FieldAccessor[] fields; + + /** Get the FieldAccessor for a particular field. */ + private FieldAccessor getField(FieldDescriptor field) { + if (field.getContainingType() != descriptor) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } else if (field.isExtension()) { + // If this type had extensions, it would subclass ExtendableMessage, + // which overrides the reflection interface to handle extensions. + throw new IllegalArgumentException( + "This type does not have extensions."); + } + return fields[field.getIndex()]; + } + + /** + * Abstract interface that provides access to a single field. This is + * implemented differently depending on the field type and cardinality. + */ + private static interface FieldAccessor { + Object get(GeneratedMessage message); + void set(GeneratedMessage.Builder builder, Object value); + Object getRepeated(GeneratedMessage message, int index); + void setRepeated(GeneratedMessage.Builder builder, + int index, Object value); + void addRepeated(GeneratedMessage.Builder builder, Object value); + boolean has(GeneratedMessage message); + int getRepeatedCount(GeneratedMessage message); + void clear(GeneratedMessage.Builder builder); + Message.Builder newBuilder(); + } + + // --------------------------------------------------------------- + + private static class SingularFieldAccessor implements FieldAccessor { + SingularFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); + type = getMethod.getReturnType(); + setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); + hasMethod = + getMethodOrDie(messageClass, "has" + camelCaseName); + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + } + + // Note: We use Java reflection to call public methods rather than + // access private fields directly as this avoids runtime security + // checks. + Class type; + Method getMethod; + Method setMethod; + Method hasMethod; + Method clearMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + public void set(GeneratedMessage.Builder builder, Object value) { + invokeOrDie(setMethod, builder, value); + } + public Object getRepeated(GeneratedMessage message, int index) { + throw new UnsupportedOperationException( + "getRepeatedField() called on a singular field."); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + throw new UnsupportedOperationException( + "setRepeatedField() called on a singular field."); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + throw new UnsupportedOperationException( + "addRepeatedField() called on a singular field."); + } + public boolean has(GeneratedMessage message) { + return (Boolean)invokeOrDie(hasMethod, message); + } + public int getRepeatedCount(GeneratedMessage message) { + throw new UnsupportedOperationException( + "getRepeatedFieldSize() called on a singular field."); + } + public void clear(GeneratedMessage.Builder builder) { + invokeOrDie(clearMethod, builder); + } + public Message.Builder newBuilder() { + throw new UnsupportedOperationException( + "newBuilderForField() called on a non-Message type."); + } + } + + private static class RepeatedFieldAccessor implements FieldAccessor { + RepeatedFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); + + getRepeatedMethod = + getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); + type = getRepeatedMethod.getReturnType(); + setRepeatedMethod = + getMethodOrDie(builderClass, "set" + camelCaseName, + Integer.TYPE, type); + addRepeatedMethod = + getMethodOrDie(builderClass, "add" + camelCaseName, type); + getCountMethod = + getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); + + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + } + + Class type; + Method getMethod; + Method getRepeatedMethod; + Method setRepeatedMethod; + Method addRepeatedMethod; + Method getCountMethod; + Method clearMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + public void set(GeneratedMessage.Builder builder, Object value) { + // Add all the elements individually. This serves two purposes: + // 1) Verifies that each element has the correct type. + // 2) Insures that the caller cannot modify the list later on and + // have the modifications be reflected in the message. + clear(builder); + for (Object element : (List)value) { + addRepeated(builder, element); + } + } + public Object getRepeated(GeneratedMessage message, int index) { + return invokeOrDie(getRepeatedMethod, message, index); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + invokeOrDie(setRepeatedMethod, builder, index, value); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + invokeOrDie(addRepeatedMethod, builder, value); + } + public boolean has(GeneratedMessage message) { + throw new UnsupportedOperationException( + "hasField() called on a singular field."); + } + public int getRepeatedCount(GeneratedMessage message) { + return (Integer)invokeOrDie(getCountMethod, message); + } + public void clear(GeneratedMessage.Builder builder) { + invokeOrDie(clearMethod, builder); + } + public Message.Builder newBuilder() { + throw new UnsupportedOperationException( + "newBuilderForField() called on a non-Message type."); + } + } + + // --------------------------------------------------------------- + + private static final class SingularEnumFieldAccessor + extends SingularFieldAccessor { + SingularEnumFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + valueOfMethod = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + getValueDescriptorMethod = + getMethodOrDie(type, "getValueDescriptor"); + } + + private Method valueOfMethod; + private Method getValueDescriptorMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getValueDescriptorMethod, super.get(message)); + } + public void set(GeneratedMessage.Builder builder, Object value) { + super.set(builder, invokeOrDie(valueOfMethod, null, value)); + } + } + + private static final class RepeatedEnumFieldAccessor + extends RepeatedFieldAccessor { + RepeatedEnumFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + valueOfMethod = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + getValueDescriptorMethod = + getMethodOrDie(type, "getValueDescriptor"); + } + + private Method valueOfMethod; + private Method getValueDescriptorMethod; + + @SuppressWarnings("unchecked") + public Object get(GeneratedMessage message) { + List newList = new ArrayList(); + for (Object element : (List)super.get(message)) { + newList.add(invokeOrDie(getValueDescriptorMethod, element)); + } + return Collections.unmodifiableList(newList); + } + public Object getRepeated(GeneratedMessage message, int index) { + return invokeOrDie(getValueDescriptorMethod, + super.getRepeated(message, index)); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, value)); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); + } + } + + // --------------------------------------------------------------- + + private static final class SingularMessageFieldAccessor + extends SingularFieldAccessor { + SingularMessageFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + newBuilderMethod = getMethodOrDie(type, "newBuilder"); + } + + private Method newBuilderMethod; + + private Object coerceType(Object value) { + if (type.isInstance(value)) { + return value; + } else { + // The value is not the exact right message type. However, if it + // is an alternative implementation of the same type -- e.g. a + // DynamicMessage -- we should accept it. In this case we can make + // a copy of the message. + return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message)value).build(); + } + } + + public void set(GeneratedMessage.Builder builder, Object value) { + super.set(builder, coerceType(value)); + } + public Message.Builder newBuilder() { + return (Message.Builder)invokeOrDie(newBuilderMethod, null); + } + } + + private static final class RepeatedMessageFieldAccessor + extends RepeatedFieldAccessor { + RepeatedMessageFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + newBuilderMethod = getMethodOrDie(type, "newBuilder"); + } + + private Method newBuilderMethod; + + private Object coerceType(Object value) { + if (type.isInstance(value)) { + return value; + } else { + // The value is not the exact right message type. However, if it + // is an alternative implementation of the same type -- e.g. a + // DynamicMessage -- we should accept it. In this case we can make + // a copy of the message. + return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message)value).build(); + } + } + + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + super.setRepeated(builder, index, coerceType(value)); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + super.addRepeated(builder, coerceType(value)); + } + public Message.Builder newBuilder() { + return (Message.Builder)invokeOrDie(newBuilderMethod, null); + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java new file mode 100644 index 00000000..d57da4ed --- /dev/null +++ b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java @@ -0,0 +1,77 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import java.io.IOException; + +/** + * Thrown when a protocol message being parsed is invalid in some way, + * e.g. it contains a malformed varint or a negative byte length. + * + * @author kenton@google.com Kenton Varda + */ +public class InvalidProtocolBufferException extends IOException { + public InvalidProtocolBufferException(String description) { + super(description); + } + + static InvalidProtocolBufferException truncatedMessage() { + return new InvalidProtocolBufferException( + "While parsing a protocol message, the input ended unexpectedly " + + "in the middle of a field. This could mean either than the " + + "input has been truncated or that an embedded message " + + "misreported its own length."); + } + + static InvalidProtocolBufferException negativeSize() { + return new InvalidProtocolBufferException( + "CodedInputStream encountered an embedded string or message " + + "which claimed to have negative size."); + } + + static InvalidProtocolBufferException malformedVarint() { + return new InvalidProtocolBufferException( + "CodedInputStream encountered a malformed varint."); + } + + static InvalidProtocolBufferException invalidTag() { + return new InvalidProtocolBufferException( + "Protocol message contained an invalid tag (zero)."); + } + + static InvalidProtocolBufferException invalidEndTag() { + return new InvalidProtocolBufferException( + "Protocol message end-group tag did not match expected tag."); + } + + static InvalidProtocolBufferException invalidWireType() { + return new InvalidProtocolBufferException( + "Protocol message tag had invalid wire type."); + } + + static InvalidProtocolBufferException recursionLimitExceeded() { + return new InvalidProtocolBufferException( + "Protocol message had too many levels of nesting. May be malicious. " + + "Use CodedInputStream.setRecursionLimit() to increase the depth limit."); + } + + static InvalidProtocolBufferException sizeLimitExceeded() { + return new InvalidProtocolBufferException( + "Protocol message was too large. May be malicious. " + + "Use CodedInputStream.setSizeLimit() to increase the size limit."); + } +} diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java new file mode 100644 index 00000000..f45f7df9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -0,0 +1,415 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO(kenton): Use generics? E.g. Builder, then +// mergeFrom*() could return BuilderType for better type-safety. + +package com.google.protobuf; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +/** + * Abstract interface implemented by Protocol Message objects. + * + * @author kenton@google.com Kenton Varda + */ +public interface Message { + /** + * Get the message's type's descriptor. This differs from the + * {@code getDescriptor()} method of generated message classes in that + * this method is an abstract method of the {@code Message} interface + * whereas {@code getDescriptor()} is a static method of a specific class. + * They return the same thing. + */ + Descriptors.Descriptor getDescriptorForType(); + + /** + * Get an instance of the type with all fields set to their default values. + * This may or may not be a singleton. This differs from the + * {@code getDefaultInstance()} method of generated message classes in that + * this method is an abstract method of the {@code Message} interface + * whereas {@code getDefaultInstance()} is a static method of a specific + * class. They return the same thing. + */ + Message getDefaultInstanceForType(); + + /** + * Returns a collection of all the fields in this message which are set + * and their corresponding values. A singular ("required" or "optional") + * field is set iff hasField() returns true for that field. A "repeated" + * field is set iff getRepeatedFieldSize() is greater than zero. The + * values are exactly what would be returned by calling + * {@link #getField(Descriptors.FieldDescriptor)} for each field. The map + * is guaranteed to be a sorted map, so iterating over it will return fields + * in order by field number. + */ + Map getAllFields(); + + /** + * Returns true if the given field is set. This is exactly equivalent to + * calling the generated "has" accessor method corresponding to the field. + * @throws IllegalArgumentException The field is a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + boolean hasField(Descriptors.FieldDescriptor field); + + /** + * Obtains the value of the given field, or the default value if it is + * not set. For primitive fields, the boxed primitive value is returned. + * For enum fields, the EnumValueDescriptor for the value is returend. For + * embedded message fields, the sub-message is returned. For repeated + * fields, a java.util.List is returned. + */ + Object getField(Descriptors.FieldDescriptor field); + + /** + * Gets the number of elements of a repeated field. This is exactly + * equivalent to calling the generated "Count" accessor method corresponding + * to the field. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); + + /** + * Gets an element of a repeated field. For primitive fields, the boxed + * primitive value is returned. For enum fields, the EnumValueDescriptor + * for the value is returend. For embedded message fields, the sub-message + * is returned. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); + + /** Get the {@link UnknownFieldSet} for this message. */ + UnknownFieldSet getUnknownFields(); + + /** + * Returns true if all required fields in the message and all embedded + * messages are set, false otherwise. + */ + boolean isInitialized(); + + /** + * Serializes the message and writes it to {@code output}. This does not + * flush or close the stream. + */ + void writeTo(CodedOutputStream output) throws IOException; + + /** + * Get the number of bytes required to encode this message. The result + * is only computed on the first call and memoized after that. + */ + int getSerializedSize(); + + // ----------------------------------------------------------------- + // Comparison and hashing + + /** + * Compares the specified object with this message for equality. Returns + * true if the given object is a message of the same type (as + * defined by {@code getDescriptorForType()}) and has identical values for + * all of its fields. + * + * @param other object to be compared for equality with this message + * @return true if the specified object is equal to this message + */ + boolean equals(Object other); + + /** + * Returns the hash code value for this message. The hash code of a message + * is defined to be getDescriptor().hashCode() ^ map.hashCode(), + * where map is a map of field numbers to field values. + * + * @return the hash code value for this message + * @see Map#hashCode() + */ + int hashCode(); + + // ----------------------------------------------------------------- + // Convenience methods. + + /** + * Converts the message to a string in protocol buffer text format. This is + * just a trivial wrapper around {@link TextFormat#printToString(Message)}. + */ + String toString(); + + /** + * Serializes the message to a {@code ByteString} and returns it. This is + * just a trivial wrapper around + * {@link #writeTo(CodedOutputStream)}. + */ + ByteString toByteString(); + + /** + * Serializes the message to a {@code byte} array and returns it. This is + * just a trivial wrapper around + * {@link #writeTo(CodedOutputStream)}. + */ + byte[] toByteArray(); + + /** + * Serializes the message and writes it to {@code output}. This is just a + * trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does + * not flush or close the stream. + */ + void writeTo(OutputStream output) throws IOException; + + // ================================================================= + // Builders + + /** + * Constructs a new builder for a message of the same type as this message. + */ + Builder newBuilderForType(); + + /** + * Abstract interface implemented by Protocol Message builders. + */ + public static interface Builder extends Cloneable { + /** Resets all fields to their default values. */ + Builder clear(); + + /** + * Merge {@code other} into the message being built. {@code other} must + * have the exact same type as {@code this} (i.e. + * {@code getDescriptorForType() == other.getDescriptorForType()}). + * + * Merging occurs as follows. For each field:
+ * * For singular primitive fields, if the field is set in {@code other}, + * then {@code other}'s value overwrites the value in this message.
+ * * For singular message fields, if the field is set in {@code other}, + * it is merged into the corresponding sub-message of this message + * using the same merging rules.
+ * * For repeated fields, the elements in {@code other} are concatenated + * with the elements in this message. + * + * This is equivalent to the {@code Message::MergeFrom} method in C++. + */ + Builder mergeFrom(Message other); + + /** + * Construct the final message. Once this is called, the Builder is no + * longer valid, and calling any other method may throw a + * NullPointerException. If you need to continue working with the builder + * after calling {@code build()}, {@code clone()} it first. + * @throws UninitializedMessageException The message is missing one or more + * required fields (i.e. {@link #isInitialized()} returns false). + * Use {@link #buildPartial()} to bypass this check. + */ + Message build(); + + /** + * Like {@link #build()}, but does not throw an exception if the message + * is missing required fields. Instead, a partial message is returned. + */ + Message buildPartial(); + + /** + * Clones the Builder. + * @see Object#clone() + */ + Builder clone(); + + /** + * Returns true if all required fields in the message and all embedded + * messages are set, false otherwise. + */ + boolean isInitialized(); + + /** + * Parses a message of this type from the input and merges it with this + * message, as if using {@link Builder#mergeFrom(Message)}. + * + *

Warning: This does not verify that all required fields are present in + * the input message. If you call {@link #build()} without setting all + * required fields, it will throw an {@link UninitializedMessageException}, + * which is a {@code RuntimeException} and thus might not be caught. There + * are a few good ways to deal with this: + *

    + *
  • Call {@link #isInitialized()} to verify that all required fields + * are set before building. + *
  • Parse the message separately using one of the static + * {@code parseFrom} methods, then use {@link #mergeFrom(Message)} + * to merge it with this one. {@code parseFrom} will throw an + * {@link InvalidProtocolBufferException} (an {@code IOException}) + * if some required fields are missing. + *
  • Use {@code buildPartial()} to build, which ignores missing + * required fields. + *
+ * + *

Note: The caller should call + * {@link CodedInputStream#checkLastTagWas(int)} after calling this to + * verify that the last tag seen was the appropriate end-group tag, + * or zero for EOF. + */ + Builder mergeFrom(CodedInputStream input) throws IOException; + + /** + * Like {@link Builder#mergeFrom(CodedInputStream)}, but also + * parses extensions. The extensions that you want to be able to parse + * must be registered in {@code extensionRegistry}. Extensions not in + * the registry will be treated as unknown fields. + */ + Builder mergeFrom(CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException; + + /** + * Get the message's type's descriptor. + * See {@link Message#getDescriptorForType()}. + */ + Descriptors.Descriptor getDescriptorForType(); + + /** + * Get the message's type's default instance. + * See {@link Message#getDefaultInstanceForType()}. + */ + Message getDefaultInstanceForType(); + + /** + * Like {@link Message#getAllFields()}. The returned map may or may not + * reflect future changes to the builder. Either way, the returned map is + * itself unmodifiable. + */ + Map getAllFields(); + + /** + * Create a Builder for messages of the appropriate type for the given + * field. Messages built with this can then be passed to setField(), + * setRepeatedField(), or addRepeatedField(). + */ + Builder newBuilderForField(Descriptors.FieldDescriptor field); + + /** Like {@link Message#hasField(Descriptors.FieldDescriptor)} */ + boolean hasField(Descriptors.FieldDescriptor field); + + /** Like {@link Message#getField(Descriptors.FieldDescriptor)} */ + Object getField(Descriptors.FieldDescriptor field); + + /** + * Sets a field to the given value. The value must be of the correct type + * for this field, i.e. the same type that + * {@link Message#getField(Descriptors.FieldDescriptor)} would return. + */ + Builder setField(Descriptors.FieldDescriptor field, Object value); + + /** + * Clears the field. This is exactly equivalent to calling the generated + * "clear" accessor method corresponding to the field. + */ + Builder clearField(Descriptors.FieldDescriptor field); + + /** + * Like {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)} + */ + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); + + /** + * Like {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} + */ + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); + + /** + * Sets an element of a repeated field to the given value. The value must + * be of the correct type for this field, i.e. the same type that + * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would + * return. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Builder setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value); + + /** + * Like {@code setRepeatedField}, but appends the value as a new element. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value); + + /** Get the {@link UnknownFieldSet} for this message. */ + UnknownFieldSet getUnknownFields(); + + /** Set the {@link UnknownFieldSet} for this message. */ + Builder setUnknownFields(UnknownFieldSet unknownFields); + + /** + * Merge some unknown fields into the {@link UnknownFieldSet} for this + * message. + */ + Builder mergeUnknownFields(UnknownFieldSet unknownFields); + + // --------------------------------------------------------------- + // Convenience methods. + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + */ + Builder mergeFrom(ByteString data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException; + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + */ + Builder mergeFrom(byte[] data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException; + + /** + * Parse a message of this type from {@code input} and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. Note that this method always + * reads the entire input (unless it throws an exception). If you + * want it to stop earlier, you will need to wrap your input in some + * wrapper stream that limits reading. Despite usually reading the entire + * input, this does not close the stream. + */ + Builder mergeFrom(InputStream input) throws IOException; + + /** + * Parse a message of this type from {@code input} and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + */ + Builder mergeFrom(InputStream input, + ExtensionRegistry extensionRegistry) + throws IOException; + } +} diff --git a/java/src/main/java/com/google/protobuf/RpcCallback.java b/java/src/main/java/com/google/protobuf/RpcCallback.java new file mode 100644 index 00000000..a99077fa --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcCallback.java @@ -0,0 +1,27 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +/** + * Interface for an RPC callback, normally called when an RPC completes. + * {@code ParameterType} is normally the method's response message type. + * + * @author kenton@google.com Kenton Varda + */ +public interface RpcCallback { + void run(ParameterType parameter); +} diff --git a/java/src/main/java/com/google/protobuf/RpcChannel.java b/java/src/main/java/com/google/protobuf/RpcChannel.java new file mode 100644 index 00000000..1c7dcfc0 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcChannel.java @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +/** + *

Abstract interface for an RPC channel. An {@code RpcChannel} represents a + * communication line to a {@link Service} which can be used to call that + * {@link Service}'s methods. The {@link Service} may be running on another + * machine. Normally, you should not call an {@code RpcChannel} directly, but + * instead construct a stub {@link Service} wrapping it. Example: + * + *

+ *   RpcChannel channel = rpcImpl.newChannel("remotehost.example.com:1234");
+ *   RpcController controller = rpcImpl.newController();
+ *   MyService service = MyService.newStub(channel);
+ *   service.myMethod(controller, request, callback);
+ * 
+ * + * @author kenton@google.com Kenton Varda + */ +public interface RpcChannel { + /** + * Call the given method of the remote service. This method is similar to + * {@code Service.callMethod()} with one important difference: the caller + * decides the types of the {@code Message} objects, not the callee. The + * request may be of any type as long as + * {@code request.getDescriptor() == method.getInputType()}. + * The response passed to the callback will be of the same type as + * {@code responsePrototype} (which must have + * {@code getDescriptor() == method.getOutputType()}). + */ + void callMethod(Descriptors.MethodDescriptor method, + RpcController controller, + Message request, + Message responsePrototype, + RpcCallback done); +} diff --git a/java/src/main/java/com/google/protobuf/RpcController.java b/java/src/main/java/com/google/protobuf/RpcController.java new file mode 100644 index 00000000..7495bb8a --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcController.java @@ -0,0 +1,98 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +/** + *

An {@code RpcController} mediates a single method call. The primary + * purpose of the controller is to provide a way to manipulate settings + * specific to the RPC implementation and to find out about RPC-level errors. + * + *

The methods provided by the {@code RpcController} interface are intended + * to be a "least common denominator" set of features which we expect all + * implementations to support. Specific implementations may provide more + * advanced features (e.g. deadline propagation). + * + * @author kenton@google.com Kenton Varda + */ +public interface RpcController { + // ----------------------------------------------------------------- + // These calls may be made from the client side only. Their results + // are undefined on the server side (may throw RuntimeExceptions). + + /** + * Resets the RpcController to its initial state so that it may be reused in + * a new call. This can be called from the client side only. It must not + * be called while an RPC is in progress. + */ + void reset(); + + /** + * After a call has finished, returns true if the call failed. The possible + * reasons for failure depend on the RPC implementation. {@code failed()} + * most only be called on the client side, and must not be called before a + * call has finished. + */ + boolean failed(); + + /** + * If {@code failed()} is {@code true}, returns a human-readable description + * of the error. + */ + String errorText(); + + /** + * Advises the RPC system that the caller desires that the RPC call be + * canceled. The RPC system may cancel it immediately, may wait awhile and + * then cancel it, or may not even cancel the call at all. If the call is + * canceled, the "done" callback will still be called and the RpcController + * will indicate that the call failed at that time. + */ + void startCancel(); + + // ----------------------------------------------------------------- + // These calls may be made from the server side only. Their results + // are undefined on the client side (may throw RuntimeExceptions). + + /** + * Causes {@code failed()} to return true on the client side. {@code reason} + * will be incorporated into the message returned by {@code errorText()}. + * If you find you need to return machine-readable information about + * failures, you should incorporate it into your response protocol buffer + * and should NOT call {@code setFailed()}. + */ + void setFailed(String reason); + + /** + * If {@code true}, indicates that the client canceled the RPC, so the server + * may as well give up on replying to it. This method must be called on the + * server side only. The server should still call the final "done" callback. + */ + boolean isCanceled(); + + /** + * Asks that the given callback be called when the RPC is canceled. The + * parameter passed to the callback will always be {@code null}. The + * callback will always be called exactly once. If the RPC completes without + * being canceled, the callback will be called after completion. If the RPC + * has already been canceled when NotifyOnCancel() is called, the callback + * will be called immediately. + * + *

{@code notifyOnCancel()} must be called no more than once per request. + * It must be called on the server side only. + */ + void notifyOnCancel(RpcCallback callback); +} diff --git a/java/src/main/java/com/google/protobuf/RpcUtil.java b/java/src/main/java/com/google/protobuf/RpcUtil.java new file mode 100644 index 00000000..13cd4aca --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcUtil.java @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +/** + * Grab-bag of utility functions useful when dealing with RPCs. + * + * @author kenton@google.com Kenton Varda + */ +public final class RpcUtil { + private RpcUtil() {} + + /** + * Take an {@code RcpCallabck} and convert it to an + * {@code RpcCallback} accepting a specific message type. This is always + * type-safe (parameter type contravariance). + */ + @SuppressWarnings("unchecked") + public static RpcCallback + specializeCallback(final RpcCallback originalCallback) { + return (RpcCallback)originalCallback; + // The above cast works, but only due to technical details of the Java + // implementation. A more theoretically correct -- but less efficient -- + // implementation would be as follows: + // return new RpcCallback() { + // public void run(Type parameter) { + // originalCallback.run(parameter); + // } + // }; + } + + /** + * Take an {@code RcpCallabck} accepting a specific message type and convert + * it to an {@code RcpCallabck}. The generalized callback will + * accept any message object which has the same descriptor, and will convert + * it to the correct class before calling the original callback. However, + * if the generalized callback is given a message with a different descriptor, + * an exception will be thrown. + */ + public static + RpcCallback generalizeCallback( + final RpcCallback originalCallback, + final Class originalClass, + final Type defaultInstance) { + return new RpcCallback() { + public void run(Message parameter) { + Type typedParameter; + try { + typedParameter = originalClass.cast(parameter); + } catch (ClassCastException e) { + typedParameter = copyAsType(defaultInstance, parameter); + } + originalCallback.run(typedParameter); + } + }; + } + + /** + * Creates a new message of type "Type" which is a copy of "source". "source" + * must have the same descriptor but may be a different class (e.g. + * DynamicMessage). + */ + @SuppressWarnings("unchecked") + private static Type copyAsType( + Type typeDefaultInstance, Message source) { + return (Type)typeDefaultInstance.newBuilderForType() + .mergeFrom(source) + .build(); + } + + /** + * Creates a callback which can only be called once. This may be useful for + * security, when passing a callback to untrusted code: most callbacks do + * not expect to be called more than once, so doing so may expose bugs if it + * is not prevented. + */ + public static + RpcCallback newOneTimeCallback( + final RpcCallback originalCallback) { + return new RpcCallback() { + boolean alreadyCalled = false; + public void run(ParameterType parameter) { + synchronized(this) { + if (alreadyCalled) { + throw new AlreadyCalledException(); + } + alreadyCalled = true; + } + + originalCallback.run(parameter); + } + }; + } + + /** + * Exception thrown when a one-time callback is called more than once. + */ + public static final class AlreadyCalledException extends RuntimeException { + public AlreadyCalledException() { + super("This RpcCallback was already called and cannot be called " + + "multiple times."); + } + } +} diff --git a/java/src/main/java/com/google/protobuf/Service.java b/java/src/main/java/com/google/protobuf/Service.java new file mode 100644 index 00000000..53b2557e --- /dev/null +++ b/java/src/main/java/com/google/protobuf/Service.java @@ -0,0 +1,97 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +/** + * Abstract base interface for protocol-buffer-based RPC services. Services + * themselves are abstract classes (implemented either by servers or as + * stubs), but they subclass this base interface. The methods of this + * interface can be used to call the methods of the service without knowing + * its exact type at compile time (analogous to the Message interface). + * + * @author kenton@google.com Kenton Varda + */ +public interface Service { + /** + * Get the {@code ServiceDescriptor} describing this service and its methods. + */ + Descriptors.ServiceDescriptor getDescriptorForType(); + + /** + *

Call a method of the service specified by MethodDescriptor. This is + * normally implemented as a simple {@code switch()} that calls the standard + * definitions of the service's methods. + * + *

Preconditions: + *

    + *
  • {@code method.getService() == getDescriptorForType()} + *
  • {@code request} is of the exact same class as the object returned by + * {@code getRequestPrototype(method)}. + *
  • {@code controller} is of the correct type for the RPC implementation + * being used by this Service. For stubs, the "correct type" depends + * on the RpcChannel which the stub is using. Server-side Service + * implementations are expected to accept whatever type of + * {@code RpcController} the server-side RPC implementation uses. + *
+ * + *

Postconditions: + *

    + *
  • {@code done} will be called when the method is complete. This may be + * before {@code callMethod()} returns or it may be at some point in + * the future. + *
  • The parameter to {@code done} is the response. It must be of the + * exact same type as would be returned by + * {@code getResponsePrototype(method)}. + *
  • If the RPC failed, the parameter to {@code done} will be + * {@code null}. Further details about the failure can be found by + * querying {@code controller}. + *
+ */ + void callMethod(Descriptors.MethodDescriptor method, + RpcController controller, + Message request, + RpcCallback done); + + /** + *

{@code callMethod()} requires that the request passed in is of a + * particular subclass of {@code Message}. {@code getRequestPrototype()} + * gets the default instances of this type for a given method. You can then + * call {@code Message.newBuilderForType()} on this instance to + * construct a builder to build an object which you can then pass to + * {@code callMethod()}. + * + *

Example: + *

+   *   MethodDescriptor method =
+   *     service.getDescriptorForType().findMethodByName("Foo");
+   *   Message request =
+   *     stub.getRequestPrototype(method).newBuilderForType()
+   *         .mergeFrom(input).build();
+   *   service.callMethod(method, request, callback);
+   * 
+ */ + Message getRequestPrototype(Descriptors.MethodDescriptor method); + + /** + * Like {@code getRequestPrototype()}, but gets a prototype of the response + * message. {@code getResponsePrototype()} is generally not needed because + * the {@code Service} implementation constructs the response message itself, + * but it may be useful in some cases to know ahead of time what type of + * object will be returned. + */ + Message getResponsePrototype(Descriptors.MethodDescriptor method); +} diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java new file mode 100644 index 00000000..c4fdfe64 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -0,0 +1,1242 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; + +import java.io.IOException; +import java.nio.CharBuffer; +import java.math.BigInteger; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Provide ascii text parsing and formatting support for proto2 instances. + * The implementation largely follows google/protobuf/text_format.cc. + * + * @author wenboz@google.com Wenbo Zhu + * @author kenton@google.com Kenton Varda + */ +public final class TextFormat { + + /** + * Outputs a textual representation of the Protocol Message supplied into + * the parameter output. (This representation is the new version of the + * classic "ProtocolPrinter" output from the original Protocol Buffer system) + */ + public static void print(Message message, Appendable output) + throws IOException { + TextGenerator generator = new TextGenerator(output); + print(message, generator); + } + + /** Outputs a textual representation of {@code fields} to {@code output}. */ + public static void print(UnknownFieldSet fields, Appendable output) + throws IOException { + TextGenerator generator = new TextGenerator(output); + printUnknownFields(fields, generator); + } + + /** + * Like {@code print()}, but writes directly to a {@code String} and + * returns it. + */ + public static String printToString(Message message) { + try { + StringBuilder text = new StringBuilder(); + print(message, text); + return text.toString(); + } catch (IOException e) { + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); + } + } + + /** + * Like {@code print()}, but writes directly to a {@code String} and + * returns it. + */ + public static String printToString(UnknownFieldSet fields) { + try { + StringBuilder text = new StringBuilder(); + print(fields, text); + return text.toString(); + } catch (IOException e) { + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); + } + } + + private static void print(Message message, TextGenerator generator) + throws IOException { + Descriptor descriptor = message.getDescriptorForType(); + for (Map.Entry field : + message.getAllFields().entrySet()) { + printField(field.getKey(), field.getValue(), generator); + } + printUnknownFields(message.getUnknownFields(), generator); + } + + public static void printField(FieldDescriptor field, + Object value, + TextGenerator generator) + throws IOException { + if (field.isRepeated()) { + // Repeated field. Print each element. + for (Object element : (List) value) { + printSingleField(field, element, generator); + } + } else { + printSingleField(field, value, generator); + } + } + + private static void printSingleField(FieldDescriptor field, + Object value, + TextGenerator generator) + throws IOException { + if (field.isExtension()) { + generator.print("["); + // We special-case MessageSet elements for compatibility with proto1. + if (field.getContainingType().getOptions().getMessageSetWireFormat() + && (field.getType() == FieldDescriptor.Type.MESSAGE) + && (field.isOptional()) + // object equality + && (field.getExtensionScope() == field.getMessageType())) { + generator.print(field.getMessageType().getFullName()); + } else { + generator.print(field.getFullName()); + } + generator.print("]"); + } else { + if (field.getType() == FieldDescriptor.Type.GROUP) { + // Groups must be serialized with their original capitalization. + generator.print(field.getMessageType().getName()); + } else { + generator.print(field.getName()); + } + } + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + generator.print(" {\n"); + generator.indent(); + } else { + generator.print(": "); + } + + printFieldValue(field, value, generator); + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + generator.outdent(); + generator.print("}"); + } + generator.print("\n"); + } + + private static void printFieldValue(FieldDescriptor field, + Object value, + TextGenerator generator) + throws IOException { + switch (field.getType()) { + case INT32: + case INT64: + case SINT32: + case SINT64: + case SFIXED32: + case SFIXED64: + case FLOAT: + case DOUBLE: + case BOOL: + // Good old toString() does what we want for these types. + generator.print(value.toString()); + break; + + case UINT32: + case FIXED32: + generator.print(unsignedToString((Integer) value)); + break; + + case UINT64: + case FIXED64: + generator.print(unsignedToString((Long) value)); + break; + + case STRING: + generator.print("\""); + generator.print(escapeText((String) value)); + generator.print("\""); + break; + + case BYTES: { + generator.print("\""); + generator.print(escapeBytes((ByteString) value)); + generator.print("\""); + break; + } + + case ENUM: { + generator.print(((EnumValueDescriptor) value).getName()); + break; + } + + case MESSAGE: + case GROUP: + print((Message) value, generator); + break; + } + } + + private static void printUnknownFields(UnknownFieldSet unknownFields, + TextGenerator generator) + throws IOException { + for (Map.Entry entry : + unknownFields.asMap().entrySet()) { + String prefix = entry.getKey().toString() + ": "; + UnknownFieldSet.Field field = entry.getValue(); + + for (long value : field.getVarintList()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(unsignedToString(value)); + generator.print("\n"); + } + for (int value : field.getFixed32List()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(String.format((Locale) null, "0x%08x", value)); + generator.print("\n"); + } + for (long value : field.getFixed64List()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(String.format((Locale) null, "0x%016x", value)); + generator.print("\n"); + } + for (ByteString value : field.getLengthDelimitedList()) { + generator.print(entry.getKey().toString()); + generator.print(": \""); + generator.print(escapeBytes(value)); + generator.print("\"\n"); + } + for (UnknownFieldSet value : field.getGroupList()) { + generator.print(entry.getKey().toString()); + generator.print(" {\n"); + generator.indent(); + printUnknownFields(value, generator); + generator.outdent(); + generator.print("}\n"); + } + } + } + + /** Convert an unsigned 32-bit integer to a string. */ + private static String unsignedToString(int value) { + if (value >= 0) { + return Integer.toString(value); + } else { + return Long.toString(((long) value) & 0x00000000FFFFFFFFL); + } + } + + /** Convert an unsigned 64-bit integer to a string. */ + private static String unsignedToString(long value) { + if (value >= 0) { + return Long.toString(value); + } else { + // Pull off the most-significant bit so that BigInteger doesn't think + // the number is negative, then set it again using setBit(). + return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL) + .setBit(63).toString(); + } + } + + /** + * An inner class for writing text to the output stream. + */ + static private final class TextGenerator { + + Appendable output; + boolean atStartOfLine = true; + StringBuilder indent = new StringBuilder(); + + public TextGenerator(Appendable output) { + this.output = output; + } + + /** + * Indent text by two spaces. After calling Indent(), two spaces will be + * inserted at the beginning of each line of text. Indent() may be called + * multiple times to produce deeper indents. + */ + public void indent() { + indent.append(" "); + } + + /** + * Reduces the current indent level by two spaces, or crashes if the indent + * level is zero. + */ + public void outdent() { + int length = indent.length(); + if (length == 0) { + throw new IllegalArgumentException( + " Outdent() without matching Indent()."); + } + indent.delete(length - 2, length); + } + + /** + * Print text to the output stream. + */ + public void print(CharSequence text) throws IOException { + int size = text.length(); + int pos = 0; + + for (int i = 0; i < size; i++) { + if (text.charAt(i) == '\n') { + write(text.subSequence(pos, size), i - pos + 1); + pos = i + 1; + atStartOfLine = true; + } + } + write(text.subSequence(pos, size), size - pos); + } + + private void write(CharSequence data, int size) throws IOException { + if (size == 0) { + return; + } + if (atStartOfLine) { + atStartOfLine = false; + output.append(indent); + } + output.append(data); + } + } + + // ================================================================= + // Parsing + + /** + * Represents a stream of tokens parsed from a {@code String}. + * + *

The Java standard library provides many classes that you might think + * would be useful for implementing this, but aren't. For example: + * + *

    + *
  • {@code java.io.StreamTokenizer}: This almost does what we want -- or, + * at least, something that would get us close to what we want -- except + * for one fatal flaw: It automatically un-escapes strings using Java + * escape sequences, which do not include all the escape sequences we + * need to support (e.g. '\x'). + *
  • {@code java.util.Scanner}: This seems like a great way at least to + * parse regular expressions out of a stream (so we wouldn't have to load + * the entire input into a single string before parsing). Sadly, + * {@code Scanner} requires that tokens be delimited with some delimiter. + * Thus, although the text "foo:" should parse to two tokens ("foo" and + * ":"), {@code Scanner} would recognize it only as a single token. + * Furthermore, {@code Scanner} provides no way to inspect the contents + * of delimiters, making it impossible to keep track of line and column + * numbers. + *
+ * + *

Luckily, Java's regular expression support does manage to be useful to + * us. (Barely: We need {@code Matcher.usePattern()}, which is new in + * Java 1.5.) So, we can use that, at least. Unfortunately, this implies + * that we need to have the entire input in one contiguous string. + */ + private static final class Tokenizer { + private final CharSequence text; + private final Matcher matcher; + private String currentToken; + + // The character index within this.text at which the current token begins. + private int pos = 0; + + // The line and column numbers of the current token. + private int line = 0; + private int column = 0; + + // The line and column numbers of the previous token (allows throwing + // errors *after* consuming). + private int previousLine = 0; + private int previousColumn = 0; + + private static Pattern WHITESPACE = Pattern.compile("(\\s|(#[^\n]*$))*"); + private static Pattern TOKEN = Pattern.compile( + "[a-zA-Z_][0-9a-zA-Z_+-]*|" + // an identifier + "[0-9+-][0-9a-zA-Z_.+-]*|" + // a number + "\"([^\"\n\\\\]|\\\\[^\n])*(\"|\\\\?$)|" + // a double-quoted string + "\'([^\"\n\\\\]|\\\\[^\n])*(\'|\\\\?$)"); // a single-quoted string + + /** Construct a tokenizer that parses tokens from the given text. */ + public Tokenizer(CharSequence text) { + this.text = text; + this.matcher = WHITESPACE.matcher(text); + skipWhitespace(); + nextToken(); + } + + /** Are we at the end of the input? */ + public boolean atEnd() { + return currentToken.length() == 0; + } + + /** Advance to the next token. */ + public void nextToken() { + previousLine = line; + previousColumn = column; + + // Advance the line counter to the current position. + while (pos < matcher.regionStart()) { + if (text.charAt(pos) == '\n') { + ++line; + column = 0; + } else { + ++column; + } + ++pos; + } + + // Match the next token. + if (matcher.regionStart() == matcher.regionEnd()) { + // EOF + currentToken = ""; + } else { + matcher.usePattern(TOKEN); + if (matcher.lookingAt()) { + currentToken = matcher.group(); + matcher.region(matcher.end(), matcher.regionEnd()); + } else { + // Take one character. + currentToken = String.valueOf(text.charAt(pos)); + matcher.region(pos + 1, matcher.regionEnd()); + } + + skipWhitespace(); + } + } + + /** + * Skip over any whitespace so that the matcher region starts at the next + * token. + */ + private void skipWhitespace() { + matcher.usePattern(WHITESPACE); + if (matcher.lookingAt()) { + matcher.region(matcher.end(), matcher.regionEnd()); + } + } + + /** + * If the next token exactly matches {@code token}, consume it and return + * {@code true}. Otherwise, return {@code false} without doing anything. + */ + public boolean tryConsume(String token) { + if (currentToken.equals(token)) { + nextToken(); + return true; + } else { + return false; + } + } + + /** + * If the next token exactly matches {@code token}, consume it. Otherwise, + * throw a {@link ParseException}. + */ + public void consume(String token) throws ParseException { + if (!tryConsume(token)) { + throw parseException("Expected \"" + token + "\"."); + } + } + + /** + * Returns {@code true} if the next token is an integer, but does + * not consume it. + */ + public boolean lookingAtInteger() { + if (currentToken.length() == 0) { + return false; + } + + char c = currentToken.charAt(0); + return ('0' <= c && c <= '9') || + c == '-' || c == '+'; + } + + /** + * If the next token is an identifier, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public String consumeIdentifier() throws ParseException { + for (int i = 0; i < currentToken.length(); i++) { + char c = currentToken.charAt(i); + if (('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + (c == '_') || (c == '.')) { + // OK + } else { + throw parseException("Expected identifier."); + } + } + + String result = currentToken; + nextToken(); + return result; + } + + /** + * If the next token is a 32-bit signed integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public int consumeInt32() throws ParseException { + try { + int result = parseInt32(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 32-bit unsigned integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public int consumeUInt32() throws ParseException { + try { + int result = parseUInt32(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 64-bit signed integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public long consumeInt64() throws ParseException { + try { + long result = parseInt64(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 64-bit unsigned integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public long consumeUInt64() throws ParseException { + try { + long result = parseUInt64(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a double, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public double consumeDouble() throws ParseException { + try { + double result = Double.parseDouble(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw floatParseException(e); + } + } + + /** + * If the next token is a float, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public float consumeFloat() throws ParseException { + try { + float result = Float.parseFloat(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw floatParseException(e); + } + } + + /** + * If the next token is a boolean, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public boolean consumeBoolean() throws ParseException { + if (currentToken.equals("true")) { + nextToken(); + return true; + } else if (currentToken.equals("false")) { + nextToken(); + return false; + } else { + throw parseException("Expected \"true\" or \"false\"."); + } + } + + /** + * If the next token is a string, consume it and return its (unescaped) + * value. Otherwise, throw a {@link ParseException}. + */ + public String consumeString() throws ParseException { + return consumeByteString().toStringUtf8(); + } + + /** + * If the next token is a string, consume it, unescape it as a + * {@link ByteString}, and return it. Otherwise, throw a + * {@link ParseException}. + */ + public ByteString consumeByteString() throws ParseException { + char quote = currentToken.length() > 0 ? currentToken.charAt(0) : '\0'; + if (quote != '\"' && quote != '\'') { + throw parseException("Expected string."); + } + + if (currentToken.length() < 2 || + currentToken.charAt(currentToken.length() - 1) != quote) { + throw parseException("String missing ending quote."); + } + + try { + String escaped = currentToken.substring(1, currentToken.length() - 1); + ByteString result = unescapeBytes(escaped); + nextToken(); + return result; + } catch (InvalidEscapeSequence e) { + throw parseException(e.getMessage()); + } + } + + /** + * Returns a {@link ParseException} with the current line and column + * numbers in the description, suitable for throwing. + */ + public ParseException parseException(String description) { + // Note: People generally prefer one-based line and column numbers. + return new ParseException( + (line + 1) + ":" + (column + 1) + ": " + description); + } + + /** + * Returns a {@link ParseException} with the line and column numbers of + * the previous token in the description, suitable for throwing. + */ + public ParseException parseExceptionPreviousToken(String description) { + // Note: People generally prefer one-based line and column numbers. + return new ParseException( + (previousLine + 1) + ":" + (previousColumn + 1) + ": " + description); + } + + /** + * Constructs an appropriate {@link ParseException} for the given + * {@code NumberFormatException} when trying to parse an integer. + */ + private ParseException integerParseException(NumberFormatException e) { + return parseException("Couldn't parse integer: " + e.getMessage()); + } + + /** + * Constructs an appropriate {@link ParseException} for the given + * {@code NumberFormatException} when trying to parse a float or double. + */ + private ParseException floatParseException(NumberFormatException e) { + return parseException("Couldn't parse number: " + e.getMessage()); + } + } + + /** Thrown when parsing an invalid text format message. */ + public static class ParseException extends IOException { + public ParseException(String message) { + super(message); + } + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. + */ + public static void merge(Readable input, + Message.Builder builder) + throws ParseException, IOException { + merge(input, ExtensionRegistry.getEmptyRegistry(), builder); + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. + */ + public static void merge(CharSequence input, + Message.Builder builder) + throws ParseException { + merge(input, ExtensionRegistry.getEmptyRegistry(), builder); + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. Extensions will be recognized if they are + * registered in {@code extensionRegistry}. + */ + public static void merge(Readable input, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws ParseException, IOException { + // Read the entire input to a String then parse that. + + // If StreamTokenizer were not quite so crippled, or if there were a kind + // of Reader that could read in chunks that match some particular regex, + // or if we wanted to write a custom Reader to tokenize our stream, then + // we would not have to read to one big String. Alas, none of these is + // the case. Oh well. + + merge(toStringBuilder(input), extensionRegistry, builder); + } + + private static final int BUFFER_SIZE = 4096; + + // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer) + // overhead is worthwhile + private static StringBuilder toStringBuilder(Readable input) + throws IOException { + StringBuilder text = new StringBuilder(); + CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); + while (true) { + int n = input.read(buffer); + if (n == -1) { + break; + } + buffer.flip(); + text.append(buffer, 0, n); + } + return text; + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. Extensions will be recognized if they are + * registered in {@code extensionRegistry}. + */ + public static void merge(CharSequence input, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws ParseException { + Tokenizer tokenizer = new Tokenizer(input); + + while (!tokenizer.atEnd()) { + mergeField(tokenizer, extensionRegistry, builder); + } + } + + /** + * Parse a single field from {@code tokenizer} and merge it into + * {@code builder}. + */ + private static void mergeField(Tokenizer tokenizer, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws ParseException { + FieldDescriptor field; + Descriptor type = builder.getDescriptorForType(); + ExtensionRegistry.ExtensionInfo extension = null; + + if (tokenizer.tryConsume("[")) { + // An extension. + StringBuilder name = new StringBuilder(tokenizer.consumeIdentifier()); + while (tokenizer.tryConsume(".")) { + name.append("."); + name.append(tokenizer.consumeIdentifier()); + } + + extension = extensionRegistry.findExtensionByName(name.toString()); + + if (extension == null) { + throw tokenizer.parseExceptionPreviousToken( + "Extension \"" + name + "\" not found in the ExtensionRegistry."); + } else if (extension.descriptor.getContainingType() != type) { + throw tokenizer.parseExceptionPreviousToken( + "Extension \"" + name + "\" does not extend message type \"" + + type.getFullName() + "\"."); + } + + tokenizer.consume("]"); + + field = extension.descriptor; + } else { + String name = tokenizer.consumeIdentifier(); + field = type.findFieldByName(name); + + // Group names are expected to be capitalized as they appear in the + // .proto file, which actually matches their type names, not their field + // names. + if (field == null) { + // Explicitly specify US locale so that this code does not break when + // executing in Turkey. + String lowerName = name.toLowerCase(Locale.US); + field = type.findFieldByName(lowerName); + // If the case-insensitive match worked but the field is NOT a group, + if (field != null && field.getType() != FieldDescriptor.Type.GROUP) { + field = null; + } + } + // Again, special-case group names as described above. + if (field != null && field.getType() == FieldDescriptor.Type.GROUP && + !field.getMessageType().getName().equals(name)) { + field = null; + } + + if (field == null) { + throw tokenizer.parseExceptionPreviousToken( + "Message type \"" + type.getFullName() + + "\" has no field named \"" + name + "\"."); + } + } + + Object value = null; + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + tokenizer.tryConsume(":"); // optional + + String endToken; + if (tokenizer.tryConsume("<")) { + endToken = ">"; + } else { + tokenizer.consume("{"); + endToken = "}"; + } + + Message.Builder subBuilder; + if (extension == null) { + subBuilder = builder.newBuilderForField(field); + } else { + subBuilder = extension.defaultInstance.newBuilderForType(); + } + + while (!tokenizer.tryConsume(endToken)) { + if (tokenizer.atEnd()) { + throw tokenizer.parseException( + "Expected \"" + endToken + "\"."); + } + mergeField(tokenizer, extensionRegistry, subBuilder); + } + + value = subBuilder.build(); + + } else { + tokenizer.consume(":"); + + switch (field.getType()) { + case INT32: + case SINT32: + case SFIXED32: + value = tokenizer.consumeInt32(); + break; + + case INT64: + case SINT64: + case SFIXED64: + value = tokenizer.consumeInt64(); + break; + + case UINT32: + case FIXED32: + value = tokenizer.consumeUInt32(); + break; + + case UINT64: + case FIXED64: + value = tokenizer.consumeUInt64(); + break; + + case FLOAT: + value = tokenizer.consumeFloat(); + break; + + case DOUBLE: + value = tokenizer.consumeDouble(); + break; + + case BOOL: + value = tokenizer.consumeBoolean(); + break; + + case STRING: + value = tokenizer.consumeString(); + break; + + case BYTES: + value = tokenizer.consumeByteString(); + break; + + case ENUM: { + EnumDescriptor enumType = field.getEnumType(); + + if (tokenizer.lookingAtInteger()) { + int number = tokenizer.consumeInt32(); + value = enumType.findValueByNumber(number); + if (value == null) { + throw tokenizer.parseExceptionPreviousToken( + "Enum type \"" + enumType.getFullName() + + "\" has no value with number " + number + "."); + } + } else { + String id = tokenizer.consumeIdentifier(); + value = enumType.findValueByName(id); + if (value == null) { + throw tokenizer.parseExceptionPreviousToken( + "Enum type \"" + enumType.getFullName() + + "\" has no value named \"" + id + "\"."); + } + } + + break; + } + + case MESSAGE: + case GROUP: + throw new RuntimeException("Can't get here."); + } + } + + if (field.isRepeated()) { + builder.addRepeatedField(field, value); + } else { + builder.setField(field, value); + } + } + + // ================================================================= + // Utility functions + // + // Some of these methods are package-private because Descriptors.java uses + // them. + + /** + * Escapes bytes in the format used in protocol buffer text format, which + * is the same as the format used for C string literals. All bytes + * that are not printable 7-bit ASCII characters are escaped, as well as + * backslash, single-quote, and double-quote characters. Characters for + * which no defined short-hand escape sequence is defined will be escaped + * using 3-digit octal sequences. + */ + static String escapeBytes(ByteString input) { + StringBuilder builder = new StringBuilder(input.size()); + for (int i = 0; i < input.size(); i++) { + byte b = input.byteAt(i); + switch (b) { + // Java does not recognize \a or \v, apparently. + case 0x07: builder.append("\\a" ); break; + case '\b': builder.append("\\b" ); break; + case '\f': builder.append("\\f" ); break; + case '\n': builder.append("\\n" ); break; + case '\r': builder.append("\\r" ); break; + case '\t': builder.append("\\t" ); break; + case 0x0b: builder.append("\\v" ); break; + case '\\': builder.append("\\\\"); break; + case '\'': builder.append("\\\'"); break; + case '"' : builder.append("\\\""); break; + default: + if (b >= 0x20) { + builder.append((char) b); + } else { + builder.append('\\'); + builder.append((char) ('0' + ((b >>> 6) & 3))); + builder.append((char) ('0' + ((b >>> 3) & 7))); + builder.append((char) ('0' + (b & 7))); + } + break; + } + } + return builder.toString(); + } + + /** + * Un-escape a byte sequence as escaped using + * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with + * "\x") are also recognized. + */ + static ByteString unescapeBytes(CharSequence input) + throws InvalidEscapeSequence { + byte[] result = new byte[input.length()]; + int pos = 0; + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '\\') { + if (i + 1 < input.length()) { + ++i; + c = input.charAt(i); + if (isOctal(c)) { + // Octal escape. + int code = digitValue(c); + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + ++i; + code = code * 8 + digitValue(input.charAt(i)); + } + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + ++i; + code = code * 8 + digitValue(input.charAt(i)); + } + result[pos++] = (byte)code; + } else { + switch (c) { + case 'a' : result[pos++] = 0x07; break; + case 'b' : result[pos++] = '\b'; break; + case 'f' : result[pos++] = '\f'; break; + case 'n' : result[pos++] = '\n'; break; + case 'r' : result[pos++] = '\r'; break; + case 't' : result[pos++] = '\t'; break; + case 'v' : result[pos++] = 0x0b; break; + case '\\': result[pos++] = '\\'; break; + case '\'': result[pos++] = '\''; break; + case '"' : result[pos++] = '\"'; break; + + case 'x': + // hex escape + int code = 0; + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + ++i; + code = digitValue(input.charAt(i)); + } else { + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\x' with no digits"); + } + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + ++i; + code = code * 16 + digitValue(input.charAt(i)); + } + result[pos++] = (byte)code; + break; + + default: + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\" + c + "'"); + } + } + } else { + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\' at end of string."); + } + } else { + result[pos++] = (byte)c; + } + } + + return ByteString.copyFrom(result, 0, pos); + } + + /** + * Thrown by {@link TextFormat#unescapeBytes} and + * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. + */ + static class InvalidEscapeSequence extends IOException { + public InvalidEscapeSequence(String description) { + super(description); + } + } + + /** + * Like {@link #escapeBytes(ByteString)}, but escapes a text string. + * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped + * individually as a 3-digit octal escape. Yes, it's weird. + */ + static String escapeText(String input) { + return escapeBytes(ByteString.copyFromUtf8(input)); + } + + /** + * Un-escape a text string as escaped using {@link #escapeText(String)}. + * Two-digit hex escapes (starting with "\x") are also recognized. + */ + static String unescapeText(String input) throws InvalidEscapeSequence { + return unescapeBytes(input).toStringUtf8(); + } + + /** Is this an octal digit? */ + private static boolean isOctal(char c) { + return '0' <= c && c <= '7'; + } + + /** Is this a hex digit? */ + private static boolean isHex(char c) { + return ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'); + } + + /** + * Interpret a character as a digit (in any base up to 36) and return the + * numeric value. This is like {@code Character.digit()} but we don't accept + * non-ASCII digits. + */ + private static int digitValue(char c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('a' <= c && c <= 'z') { + return c - 'a' + 10; + } else { + return c - 'A' + 10; + } + } + + /** + * Parse a 32-bit signed integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. + */ + static int parseInt32(String text) throws NumberFormatException { + return (int) parseInteger(text, true, false); + } + + /** + * Parse a 32-bit unsigned integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. The + * result is coerced to a (signed) {@code int} when returned since Java has + * no unsigned integer type. + */ + static int parseUInt32(String text) throws NumberFormatException { + return (int) parseInteger(text, false, false); + } + + /** + * Parse a 64-bit signed integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. + */ + static long parseInt64(String text) throws NumberFormatException { + return parseInteger(text, true, true); + } + + /** + * Parse a 64-bit unsigned integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. The + * result is coerced to a (signed) {@code long} when returned since Java has + * no unsigned long type. + */ + static long parseUInt64(String text) throws NumberFormatException { + return parseInteger(text, false, true); + } + + private static long parseInteger(String text, + boolean isSigned, + boolean isLong) + throws NumberFormatException { + int pos = 0; + + boolean negative = false; + if (text.startsWith("-", pos)) { + if (!isSigned) { + throw new NumberFormatException("Number must be positive: " + text); + } + ++pos; + negative = true; + } + + int radix = 10; + if (text.startsWith("0x", pos)) { + pos += 2; + radix = 16; + } else if (text.startsWith("0", pos)) { + radix = 8; + } + + String numberText = text.substring(pos); + + long result = 0; + if (numberText.length() < 16) { + // Can safely assume no overflow. + result = Long.parseLong(numberText, radix); + if (negative) { + result = -result; + } + + // Check bounds. + // No need to check for 64-bit numbers since they'd have to be 16 chars + // or longer to overflow. + if (!isLong) { + if (isSigned) { + if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) { + throw new NumberFormatException( + "Number out of range for 32-bit signed integer: " + text); + } + } else { + if (result >= (1L << 32) || result < 0) { + throw new NumberFormatException( + "Number out of range for 32-bit unsigned integer: " + text); + } + } + } + } else { + BigInteger bigValue = new BigInteger(numberText, radix); + if (negative) { + bigValue = bigValue.negate(); + } + + // Check bounds. + if (!isLong) { + if (isSigned) { + if (bigValue.bitLength() > 31) { + throw new NumberFormatException( + "Number out of range for 32-bit signed integer: " + text); + } + } else { + if (bigValue.bitLength() > 32) { + throw new NumberFormatException( + "Number out of range for 32-bit unsigned integer: " + text); + } + } + } else { + if (isSigned) { + if (bigValue.bitLength() > 63) { + throw new NumberFormatException( + "Number out of range for 64-bit signed integer: " + text); + } + } else { + if (bigValue.bitLength() > 64) { + throw new NumberFormatException( + "Number out of range for 64-bit unsigned integer: " + text); + } + } + } + + result = bigValue.longValue(); + } + + return result; + } +} diff --git a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java new file mode 100644 index 00000000..61c3e248 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java @@ -0,0 +1,146 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Thrown when attempting to build a protocol message that is missing required + * fields. This is a {@code RuntimeException} because it normally represents + * a programming error: it happens when some code which constructs a message + * fails to set all the fields. {@code parseFrom()} methods do not + * throw this; they throw an {@link InvalidProtocolBufferException} if + * required fields are missing, because it is not a programming error to + * receive an incomplete message. In other words, + * {@code UninitializedMessageException} should never be thrown by correct + * code, but {@code InvalidProtocolBufferException} might be. + * + * @author kenton@google.com Kenton Varda + */ +public class UninitializedMessageException extends RuntimeException { + public UninitializedMessageException(Message message) { + this(findMissingFields(message)); + } + + private UninitializedMessageException(List missingFields) { + super(buildDescription(missingFields)); + this.missingFields = missingFields; + } + + private final List missingFields; + + /** + * Get a list of human-readable names of required fields missing from this + * message. Each name is a full path to a field, e.g. "foo.bar[5].baz". + */ + public List getMissingFields() { + return Collections.unmodifiableList(missingFields); + } + + /** + * Converts this exception to an {@link InvalidProtocolBufferException}. + * When a parsed message is missing required fields, this should be thrown + * instead of {@code UninitializedMessageException}. + */ + public InvalidProtocolBufferException asInvalidProtocolBufferException() { + return new InvalidProtocolBufferException(getMessage()); + } + + /** Construct the description string for this exception. */ + private static String buildDescription(List missingFields) { + StringBuilder description = + new StringBuilder("Message missing required fields: "); + boolean first = true; + for (String field : missingFields) { + if (first) { + first = false; + } else { + description.append(", "); + } + description.append(field); + } + return description.toString(); + } + + /** + * Populates {@code this.missingFields} with the full "path" of each + * missing required field in the given message. + */ + private static List findMissingFields(Message message) { + List results = new ArrayList(); + findMissingFields(message, "", results); + return results; + } + + /** Recursive helper implementing {@link #findMissingFields(Message)}. */ + private static void findMissingFields(Message message, String prefix, + List results) { + for (FieldDescriptor field : message.getDescriptorForType().getFields()) { + if (field.isRequired() && !message.hasField(field)) { + results.add(prefix + field.getName()); + } + } + + for (Map.Entry entry : + message.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + Object value = entry.getValue(); + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + int i = 0; + for (Object element : (List) value) { + findMissingFields((Message) element, + subMessagePrefix(prefix, field, i++), + results); + } + } else { + if (message.hasField(field)) { + findMissingFields((Message) value, + subMessagePrefix(prefix, field, -1), + results); + } + } + } + } + } + + private static String subMessagePrefix(String prefix, + FieldDescriptor field, + int index) { + StringBuilder result = new StringBuilder(prefix); + if (field.isExtension()) { + result.append('(') + .append(field.getFullName()) + .append(')'); + } else { + result.append(field.getName()); + } + if (index != -1) { + result.append('[') + .append(index) + .append(']'); + } + result.append('.'); + return result.toString(); + } +} diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java new file mode 100644 index 00000000..6bcc4309 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -0,0 +1,746 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.TreeMap; +import java.util.List; +import java.util.Map; + +/** + * {@code UnknownFieldSet} is used to keep track of fields which were seen when + * parsing a protocol message but whose field numbers or types are unrecognized. + * This most frequently occurs when new fields are added to a message type + * and then messages containing those feilds are read by old software that was + * compiled before the new types were added. + * + *

Every {@link Message} contains an {@code UnknownFieldSet} (and every + * {@link Message.Builder} contains an {@link UnknownFieldSet.Builder}). + * + *

Most users will never need to use this class. + * + * @author kenton@google.com Kenton Varda + */ +public final class UnknownFieldSet { + private UnknownFieldSet() {} + + /** Create a new {@link UnknownFieldSet.Builder}. */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Create a new {@link UnknownFieldSet.Builder} and initialize it to be a copy + * of {@code copyFrom}. + */ + public static Builder newBuilder(UnknownFieldSet copyFrom) { + return new Builder().mergeFrom(copyFrom); + } + + /** Get an empty {@code UnknownFieldSet}. */ + public static UnknownFieldSet getDefaultInstance() { + return defaultInstance; + } + private static UnknownFieldSet defaultInstance = + new UnknownFieldSet(Collections.emptyMap()); + + /** + * Construct an {@code UnknownFieldSet} around the given map. The map is + * expected to be immutable. + */ + private UnknownFieldSet(Map fields) { + this.fields = fields; + } + private Map fields; + + /** Get a map of fields in the set by number. */ + public Map asMap() { + return fields; + } + + /** Check if the given field number is present in the set. */ + public boolean hasField(int number) { + return fields.containsKey(number); + } + + /** + * Get a field by number. Returns an empty field if not present. Never + * returns {@code null}. + */ + public Field getField(int number) { + Field result = fields.get(number); + return (result == null) ? Field.getDefaultInstance() : result; + } + + /** Serializes the set and writes it to {@code output}. */ + public void writeTo(CodedOutputStream output) throws IOException { + for (Map.Entry entry : fields.entrySet()) { + entry.getValue().writeTo(entry.getKey(), output); + } + } + + /** + * Converts the set to a string in protocol buffer text format. This is + * just a trivial wrapper around + * {@link TextFormat#printToString(UnknownFieldSet)}. + */ + public final String toString() { + return TextFormat.printToString(this); + } + + /** + * Serializes the message to a {@code ByteString} and returns it. This is + * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. + */ + public final ByteString toByteString() { + try { + ByteString.CodedBuilder out = + ByteString.newCodedBuilder(getSerializedSize()); + writeTo(out.getCodedOutput()); + return out.build(); + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + /** + * Serializes the message to a {@code byte} array and returns it. This is + * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. + */ + public final byte[] toByteArray() { + try { + byte[] result = new byte[getSerializedSize()]; + CodedOutputStream output = CodedOutputStream.newInstance(result); + writeTo(output); + output.checkNoSpaceLeft(); + return result; + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a byte array threw an IOException " + + "(should never happen).", e); + } + } + + /** + * Serializes the message and writes it to {@code output}. This is just a + * trivial wrapper around {@link #writeTo(CodedOutputStream)}. + */ + public final void writeTo(OutputStream output) throws IOException { + CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + writeTo(codedOutput); + codedOutput.flush(); + } + + /** Get the number of bytes required to encode this set. */ + public int getSerializedSize() { + int result = 0; + for (Map.Entry entry : fields.entrySet()) { + result += entry.getValue().getSerializedSize(entry.getKey()); + } + return result; + } + + /** + * Serializes the set and writes it to {@code output} using + * {@code MessageSet} wire format. + */ + public void writeAsMessageSetTo(CodedOutputStream output) + throws IOException { + for (Map.Entry entry : fields.entrySet()) { + entry.getValue().writeAsMessageSetExtensionTo( + entry.getKey(), output); + } + } + + /** + * Get the number of bytes required to encode this set using + * {@code MessageSet} wire format. + */ + public int getSerializedSizeAsMessageSet() { + int result = 0; + for (Map.Entry entry : fields.entrySet()) { + result += entry.getValue().getSerializedSizeAsMessageSetExtension( + entry.getKey()); + } + return result; + } + + /** Parse an {@code UnknownFieldSet} from the given input stream. */ + static public UnknownFieldSet parseFrom(CodedInputStream input) + throws IOException { + return newBuilder().mergeFrom(input).build(); + } + + /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ + public static UnknownFieldSet parseFrom(ByteString data) + throws InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).build(); + } + + /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ + public static UnknownFieldSet parseFrom(byte[] data) + throws InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).build(); + } + + /** Parse an {@code UnknownFieldSet} from {@code input} and return it. */ + public static UnknownFieldSet parseFrom(InputStream input) + throws IOException { + return newBuilder().mergeFrom(input).build(); + } + + /** + * Builder for {@link UnknownFieldSet}s. + * + *

Note that this class maintains {@link Field.Builder}s for all fields + * in the set. Thus, adding one element to an existing {@link Field} does not + * require making a copy. This is important for efficient parsing of + * unknown repeated fields. However, it implies that {@link Field}s cannot + * be constructed independently, nor can two {@link UnknownFieldSet}s share + * the same {@code Field} object. + * + *

Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}. + */ + public static final class Builder { + private Builder() {} + private Map fields = new TreeMap(); + + // Optimization: We keep around a builder for the last field that was + // modified so that we can efficiently add to it multiple times in a + // row (important when parsing an unknown repeated field). + int lastFieldNumber = 0; + Field.Builder lastField = null; + + /** + * Get a field builder for the given field number which includes any + * values that already exist. + */ + private Field.Builder getFieldBuilder(int number) { + if (lastField != null) { + if (number == lastFieldNumber) { + return lastField; + } + // Note: addField() will reset lastField and lastFieldNumber. + addField(lastFieldNumber, lastField.build()); + } + if (number == 0) { + return null; + } else { + Field existing = fields.get(number); + lastFieldNumber = number; + lastField = Field.newBuilder(); + if (existing != null) { + lastField.mergeFrom(existing); + } + return lastField; + } + } + + /** + * Build the {@link UnknownFieldSet} and return it. + * + *

Once {@code build()} has been called, the {@code Builder} will no + * longer be usable. Calling any method after {@code build()} will throw + * {@code NullPointerException}. + */ + public UnknownFieldSet build() { + getFieldBuilder(0); // Force lastField to be built. + UnknownFieldSet result; + if (fields.isEmpty()) { + result = getDefaultInstance(); + } else { + result = new UnknownFieldSet(Collections.unmodifiableMap(fields)); + } + fields = null; + return result; + } + + /** Reset the builder to an empty set. */ + public Builder clear() { + fields = new TreeMap(); + lastFieldNumber = 0; + lastField = null; + return this; + } + + /** + * Merge the fields from {@code other} into this set. If a field number + * exists in both sets, {@code other}'s values for that field will be + * appended to the values in this set. + */ + public Builder mergeFrom(UnknownFieldSet other) { + if (other != getDefaultInstance()) { + for (Map.Entry entry : other.fields.entrySet()) { + mergeField(entry.getKey(), entry.getValue()); + } + } + return this; + } + + /** + * Add a field to the {@code UnknownFieldSet}. If a field with the same + * number already exists, the two are merged. + */ + public Builder mergeField(int number, Field field) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + if (hasField(number)) { + getFieldBuilder(number).mergeFrom(field); + } else { + // Optimization: We could call getFieldBuilder(number).mergeFrom(field) + // in this case, but that would create a copy of the Field object. + // We'd rather reuse the one passed to us, so call addField() instead. + addField(number, field); + } + return this; + } + + /** + * Convenience method for merging a new field containing a single varint + * value. This is used in particular when an unknown enum value is + * encountered. + */ + public Builder mergeVarintField(int number, int value) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + getFieldBuilder(number).addVarint(value); + return this; + } + + /** Check if the given field number is present in the set. */ + public boolean hasField(int number) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + return number == lastFieldNumber || fields.containsKey(number); + } + + /** + * Add a field to the {@code UnknownFieldSet}. If a field with the same + * number already exists, it is removed. + */ + public Builder addField(int number, Field field) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + if (lastField != null && lastFieldNumber == number) { + // Discard this. + lastField = null; + lastFieldNumber = 0; + } + fields.put(number, field); + return this; + } + + /** + * Get all present {@code Field}s as an immutable {@code Map}. If more + * fields are added, the changes may or may not be reflected in this map. + */ + public Map asMap() { + getFieldBuilder(0); // Force lastField to be built. + return Collections.unmodifiableMap(fields); + } + + /** + * Parse an entire message from {@code input} and merge its fields into + * this set. + */ + public Builder mergeFrom(CodedInputStream input) throws IOException { + while (true) { + int tag = input.readTag(); + if (tag == 0 || !mergeFieldFrom(tag, input)) { + break; + } + } + return this; + } + + /** + * Parse a single field from {@code input} and merge it into this set. + * @param tag The field's tag number, which was already parsed. + * @return {@code false} if the tag is an engroup tag. + */ + public boolean mergeFieldFrom(int tag, CodedInputStream input) + throws IOException { + int number = WireFormat.getTagFieldNumber(tag); + switch (WireFormat.getTagWireType(tag)) { + case WireFormat.WIRETYPE_VARINT: + getFieldBuilder(number).addVarint(input.readInt32()); + return true; + case WireFormat.WIRETYPE_FIXED64: + getFieldBuilder(number).addFixed64(input.readFixed64()); + return true; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + getFieldBuilder(number).addLengthDelimited(input.readBytes()); + return true; + case WireFormat.WIRETYPE_START_GROUP: { + UnknownFieldSet.Builder subBuilder = UnknownFieldSet.newBuilder(); + input.readUnknownGroup(number, subBuilder); + getFieldBuilder(number).addGroup(subBuilder.build()); + return true; + } + case WireFormat.WIRETYPE_END_GROUP: + return false; + case WireFormat.WIRETYPE_FIXED32: + getFieldBuilder(number).addFixed32(input.readFixed32()); + return true; + default: + throw InvalidProtocolBufferException.invalidWireType(); + } + } + + /** + * Parse {@code data} as an {@code UnknownFieldSet} and merge it with the + * set being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(ByteString data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = data.newCodedInput(); + mergeFrom(input); + input.checkLastTagWas(0); + return this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + /** + * Parse {@code data} as an {@code UnknownFieldSet} and merge it with the + * set being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(byte[] data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = CodedInputStream.newInstance(data); + mergeFrom(input); + input.checkLastTagWas(0); + return this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + /** + * Parse an {@code UnknownFieldSet} from {@code input} and merge it with the + * set being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(InputStream input) throws IOException { + CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput); + codedInput.checkLastTagWas(0); + return this; + } + } + + /** + * Represents a single field in an {@code UnknownFieldSet}. + * + *

A {@code Field} consists of five lists of values. The lists correspond + * to the five "wire types" used in the protocol buffer binary format. + * The wire type of each field can be determined from the encoded form alone, + * without knowing the field's declared type. So, we are able to parse + * unknown values at least this far and separate them. Normally, only one + * of the five lists will contain any values, since it is impossible to + * define a valid message type that declares two different types for the + * same field number. However, the code is designed to allow for the case + * where the same unknown field number is encountered using multiple different + * wire types. + * + *

{@code Field} is an immutable class. To construct one, you must use a + * {@link Field.Builder}. + * + * @see UnknownFieldSet + */ + public static final class Field { + private Field() {} + + /** Construct a new {@link Builder}. */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Construct a new {@link Builder} and initialize it to a copy of + * {@code copyFrom}. + */ + public static Builder newBuilder(Field copyFrom) { + return new Builder().mergeFrom(copyFrom); + } + + /** Get an empty {@code Field}. */ + public static Field getDefaultInstance() { + return defaultInstance; + } + private static Field defaultInstance = newBuilder().build(); + + /** Get the list of varint values for this field. */ + public List getVarintList() { return varint; } + + /** Get the list of fixed32 values for this field. */ + public List getFixed32List() { return fixed32; } + + /** Get the list of fixed64 values for this field. */ + public List getFixed64List() { return fixed64; } + + /** Get the list of length-delimited values for this field. */ + public List getLengthDelimitedList() { return lengthDelimited; } + + /** + * Get the list of embedded group values for this field. These are + * represented using {@link UnknownFieldSet}s rather than {@link Message}s + * since the group's type is presumably unknown. + */ + public List getGroupList() { return group; } + + /** + * Serializes the field, including field number, and writes it to + * {@code output}. + */ + public void writeTo(int fieldNumber, CodedOutputStream output) + throws IOException { + for (long value : varint) { + output.writeUInt64(fieldNumber, value); + } + for (int value : fixed32) { + output.writeFixed32(fieldNumber, value); + } + for (long value : fixed64) { + output.writeFixed64(fieldNumber, value); + } + for (ByteString value : lengthDelimited) { + output.writeBytes(fieldNumber, value); + } + for (UnknownFieldSet value : group) { + output.writeUnknownGroup(fieldNumber, value); + } + } + + /** + * Get the number of bytes required to encode this field, including field + * number. + */ + public int getSerializedSize(int fieldNumber) { + int result = 0; + for (long value : varint) { + result += CodedOutputStream.computeUInt64Size(fieldNumber, value); + } + for (int value : fixed32) { + result += CodedOutputStream.computeFixed32Size(fieldNumber, value); + } + for (long value : fixed64) { + result += CodedOutputStream.computeFixed64Size(fieldNumber, value); + } + for (ByteString value : lengthDelimited) { + result += CodedOutputStream.computeBytesSize(fieldNumber, value); + } + for (UnknownFieldSet value : group) { + result += CodedOutputStream.computeUnknownGroupSize(fieldNumber, value); + } + return result; + } + + /** + * Serializes the field, including field number, and writes it to + * {@code output}, using {@code MessageSet} wire format. + */ + public void writeAsMessageSetExtensionTo( + int fieldNumber, + CodedOutputStream output) + throws IOException { + for (ByteString value : lengthDelimited) { + output.writeRawMessageSetExtension(fieldNumber, value); + } + } + + /** + * Get the number of bytes required to encode this field, including field + * number, using {@code MessageSet} wire format. + */ + public int getSerializedSizeAsMessageSetExtension(int fieldNumber) { + int result = 0; + for (ByteString value : lengthDelimited) { + result += CodedOutputStream.computeRawMessageSetExtensionSize( + fieldNumber, value); + } + return result; + } + + private List varint; + private List fixed32; + private List fixed64; + private List lengthDelimited; + private List group; + + /** + * Used to build a {@link Field} within an {@link UnknownFieldSet}. + * + *

Use {@link Field#newBuilder()} to construct a {@code Builder}. + */ + public static final class Builder { + private Builder() {} + private Field result = new Field(); + + /** + * Build the field. After {@code build()} has been called, the + * {@code Builder} is no longer usable. Calling any other method will + * throw a {@code NullPointerException}. + */ + public Field build() { + if (result.varint == null) { + result.varint = Collections.emptyList(); + } else { + result.varint = Collections.unmodifiableList(result.varint); + } + if (result.fixed32 == null) { + result.fixed32 = Collections.emptyList(); + } else { + result.fixed32 = Collections.unmodifiableList(result.fixed32); + } + if (result.fixed64 == null) { + result.fixed64 = Collections.emptyList(); + } else { + result.fixed64 = Collections.unmodifiableList(result.fixed64); + } + if (result.lengthDelimited == null) { + result.lengthDelimited = Collections.emptyList(); + } else { + result.lengthDelimited = + Collections.unmodifiableList(result.lengthDelimited); + } + if (result.group == null) { + result.group = Collections.emptyList(); + } else { + result.group = Collections.unmodifiableList(result.group); + } + + Field returnMe = result; + result = null; + return returnMe; + } + + /** Discard the field's contents. */ + public Builder clear() { + result = new Field(); + return this; + } + + /** + * Merge the values in {@code other} into this field. For each list + * of values, {@code other}'s values are append to the ones in this + * field. + */ + public Builder mergeFrom(Field other) { + if (!other.varint.isEmpty()) { + if (result.varint == null) { + result.varint = new ArrayList(); + } + result.varint.addAll(other.varint); + } + if (!other.fixed32.isEmpty()) { + if (result.fixed32 == null) { + result.fixed32 = new ArrayList(); + } + result.fixed32.addAll(other.fixed32); + } + if (!other.fixed64.isEmpty()) { + if (result.fixed64 == null) { + result.fixed64 = new ArrayList(); + } + result.fixed64.addAll(other.fixed64); + } + if (!other.lengthDelimited.isEmpty()) { + if (result.lengthDelimited == null) { + result.lengthDelimited = new ArrayList(); + } + result.lengthDelimited.addAll(other.lengthDelimited); + } + if (!other.group.isEmpty()) { + if (result.group == null) { + result.group = new ArrayList(); + } + result.group.addAll(other.group); + } + return this; + } + + /** Add a varint value. */ + public Builder addVarint(long value) { + if (result.varint == null) { + result.varint = new ArrayList(); + } + result.varint.add(value); + return this; + } + + /** Add a fixed32 value. */ + public Builder addFixed32(int value) { + if (result.fixed32 == null) { + result.fixed32 = new ArrayList(); + } + result.fixed32.add(value); + return this; + } + + /** Add a fixed64 value. */ + public Builder addFixed64(long value) { + if (result.fixed64 == null) { + result.fixed64 = new ArrayList(); + } + result.fixed64.add(value); + return this; + } + + /** Add a length-delimited value. */ + public Builder addLengthDelimited(ByteString value) { + if (result.lengthDelimited == null) { + result.lengthDelimited = new ArrayList(); + } + result.lengthDelimited.add(value); + return this; + } + + /** Add an embedded group. */ + public Builder addGroup(UnknownFieldSet value) { + if (result.group == null) { + result.group = new ArrayList(); + } + result.group.add(value); + return this; + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java new file mode 100644 index 00000000..31bfd402 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -0,0 +1,99 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +/** + * This class is used internally by the Protocol Buffer library and generated + * message implementations. It is public only because those generated messages + * do not reside in the {@code protocol2} package. Others should not use this + * class directly. + * + * This class contains constants and helper functions useful for dealing with + * the Protocol Buffer wire format. + * + * @author kenton@google.com Kenton Varda + */ +public final class WireFormat { + // Do not allow instantiation. + private WireFormat() {} + + static final int WIRETYPE_VARINT = 0; + static final int WIRETYPE_FIXED64 = 1; + static final int WIRETYPE_LENGTH_DELIMITED = 2; + static final int WIRETYPE_START_GROUP = 3; + static final int WIRETYPE_END_GROUP = 4; + static final int WIRETYPE_FIXED32 = 5; + + static final int TAG_TYPE_BITS = 3; + static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1; + + /** Given a tag value, determines the wire type (the lower 3 bits). */ + static int getTagWireType(int tag) { + return tag & TAG_TYPE_MASK; + } + + /** Given a tag value, determines the field number (the upper 29 bits). */ + public static int getTagFieldNumber(int tag) { + return tag >>> TAG_TYPE_BITS; + } + + /** Makes a tag value given a field number and wire type. */ + static int makeTag(int fieldNumber, int wireType) { + return (fieldNumber << TAG_TYPE_BITS) | wireType; + } + + static int getWireFormatForFieldType(Descriptors.FieldDescriptor.Type type) { + switch (type) { + case DOUBLE : return WIRETYPE_FIXED64; + case FLOAT : return WIRETYPE_FIXED32; + case INT64 : return WIRETYPE_VARINT; + case UINT64 : return WIRETYPE_VARINT; + case INT32 : return WIRETYPE_VARINT; + case FIXED64 : return WIRETYPE_FIXED64; + case FIXED32 : return WIRETYPE_FIXED32; + case BOOL : return WIRETYPE_VARINT; + case STRING : return WIRETYPE_LENGTH_DELIMITED; + case GROUP : return WIRETYPE_START_GROUP; + case MESSAGE : return WIRETYPE_LENGTH_DELIMITED; + case BYTES : return WIRETYPE_LENGTH_DELIMITED; + case UINT32 : return WIRETYPE_VARINT; + case ENUM : return WIRETYPE_VARINT; + case SFIXED32: return WIRETYPE_FIXED32; + case SFIXED64: return WIRETYPE_FIXED64; + case SINT32 : return WIRETYPE_VARINT; + case SINT64 : return WIRETYPE_VARINT; + } + + throw new RuntimeException( + "There is no way to get here, but the compiler thinks otherwise."); + } + + // Field numbers for feilds in MessageSet wire format. + static final int MESSAGE_SET_ITEM = 1; + static final int MESSAGE_SET_TYPE_ID = 2; + static final int MESSAGE_SET_MESSAGE = 3; + + // Tag numbers. + static final int MESSAGE_SET_ITEM_TAG = + makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP); + static final int MESSAGE_SET_ITEM_END_TAG = + makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP); + static final int MESSAGE_SET_TYPE_ID_TAG = + makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT); + static final int MESSAGE_SET_MESSAGE_TAG = + makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED); +} diff --git a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java new file mode 100644 index 00000000..bbb88b86 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java @@ -0,0 +1,362 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestRequiredForeign; +import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; + +import junit.framework.TestCase; + +import java.util.Map; + +/** + * Unit test for {@link AbstractMessage}. + * + * @author kenton@google.com Kenton Varda + */ +public class AbstractMessageTest extends TestCase { + /** + * Extends AbstractMessage and wraps some other message object. The methods + * of the Message interface which aren't explicitly implemented by + * AbstractMessage are forwarded to the wrapped object. This allows us to + * test that AbstractMessage's implementations work even if the wrapped + * object does not use them. + */ + private static class AbstractMessageWrapper extends AbstractMessage { + private final Message wrappedMessage; + + public AbstractMessageWrapper(Message wrappedMessage) { + this.wrappedMessage = wrappedMessage; + } + + public Descriptors.Descriptor getDescriptorForType() { + return wrappedMessage.getDescriptorForType(); + } + public AbstractMessageWrapper getDefaultInstanceForType() { + return new AbstractMessageWrapper( + wrappedMessage.getDefaultInstanceForType()); + } + public Map getAllFields() { + return wrappedMessage.getAllFields(); + } + public boolean hasField(Descriptors.FieldDescriptor field) { + return wrappedMessage.hasField(field); + } + public Object getField(Descriptors.FieldDescriptor field) { + return wrappedMessage.getField(field); + } + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + return wrappedMessage.getRepeatedFieldCount(field); + } + public Object getRepeatedField( + Descriptors.FieldDescriptor field, int index) { + return wrappedMessage.getRepeatedField(field, index); + } + public UnknownFieldSet getUnknownFields() { + return wrappedMessage.getUnknownFields(); + } + public Builder newBuilderForType() { + return new Builder(wrappedMessage.newBuilderForType()); + } + + static class Builder extends AbstractMessage.Builder { + private final Message.Builder wrappedBuilder; + + public Builder(Message.Builder wrappedBuilder) { + this.wrappedBuilder = wrappedBuilder; + } + + public AbstractMessageWrapper build() { + return new AbstractMessageWrapper(wrappedBuilder.build()); + } + public AbstractMessageWrapper buildPartial() { + return new AbstractMessageWrapper(wrappedBuilder.buildPartial()); + } + public Builder clone() { + return new Builder(wrappedBuilder.clone()); + } + public boolean isInitialized() { + return clone().buildPartial().isInitialized(); + } + public Descriptors.Descriptor getDescriptorForType() { + return wrappedBuilder.getDescriptorForType(); + } + public AbstractMessageWrapper getDefaultInstanceForType() { + return new AbstractMessageWrapper( + wrappedBuilder.getDefaultInstanceForType()); + } + public Map getAllFields() { + return wrappedBuilder.getAllFields(); + } + public Builder newBuilderForField(Descriptors.FieldDescriptor field) { + return new Builder(wrappedBuilder.newBuilderForField(field)); + } + public boolean hasField(Descriptors.FieldDescriptor field) { + return wrappedBuilder.hasField(field); + } + public Object getField(Descriptors.FieldDescriptor field) { + return wrappedBuilder.getField(field); + } + public Builder setField(Descriptors.FieldDescriptor field, Object value) { + wrappedBuilder.setField(field, value); + return this; + } + public Builder clearField(Descriptors.FieldDescriptor field) { + wrappedBuilder.clearField(field); + return this; + } + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + return wrappedBuilder.getRepeatedFieldCount(field); + } + public Object getRepeatedField( + Descriptors.FieldDescriptor field, int index) { + return wrappedBuilder.getRepeatedField(field, index); + } + public Builder setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + wrappedBuilder.setRepeatedField(field, index, value); + return this; + } + public Builder addRepeatedField( + Descriptors.FieldDescriptor field, Object value) { + wrappedBuilder.addRepeatedField(field, value); + return this; + } + public UnknownFieldSet getUnknownFields() { + return wrappedBuilder.getUnknownFields(); + } + public Builder setUnknownFields(UnknownFieldSet unknownFields) { + wrappedBuilder.setUnknownFields(unknownFields); + return this; + } + } + } + + // ================================================================= + + TestUtil.ReflectionTester reflectionTester = + new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null); + + TestUtil.ReflectionTester extensionsReflectionTester = + new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), + TestUtil.getExtensionRegistry()); + + public void testClear() throws Exception { + AbstractMessageWrapper message = + new AbstractMessageWrapper.Builder( + TestAllTypes.newBuilder(TestUtil.getAllSet())) + .clear().build(); + TestUtil.assertClear((TestAllTypes) message.wrappedMessage); + } + + public void testCopy() throws Exception { + AbstractMessageWrapper message = + new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder()) + .mergeFrom(TestUtil.getAllSet()).build(); + TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage); + } + + public void testSerializedSize() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet()); + + assertEquals(message.getSerializedSize(), + abstractMessage.getSerializedSize()); + } + + public void testSerialization() throws Exception { + Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet()); + + TestUtil.assertAllFieldsSet( + TestAllTypes.parseFrom(abstractMessage.toByteString())); + + assertEquals(TestUtil.getAllSet().toByteString(), + abstractMessage.toByteString()); + } + + public void testParsing() throws Exception { + AbstractMessageWrapper.Builder builder = + new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder()); + AbstractMessageWrapper message = + builder.mergeFrom(TestUtil.getAllSet().toByteString()).build(); + TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage); + } + + public void testOptimizedForSize() throws Exception { + // We're mostly only checking that this class was compiled successfully. + TestOptimizedForSize message = + TestOptimizedForSize.newBuilder().setI(1).build(); + message = TestOptimizedForSize.parseFrom(message.toByteString()); + assertEquals(2, message.getSerializedSize()); + } + + // ----------------------------------------------------------------- + // Tests for isInitialized(). + + private static final TestRequired TEST_REQUIRED_UNINITIALIZED = + TestRequired.getDefaultInstance(); + private static final TestRequired TEST_REQUIRED_INITIALIZED = + TestRequired.newBuilder().setA(1).setB(2).setC(3).build(); + + public void testIsInitialized() throws Exception { + TestRequired.Builder builder = TestRequired.newBuilder(); + AbstractMessageWrapper.Builder abstractBuilder = + new AbstractMessageWrapper.Builder(builder); + + assertFalse(abstractBuilder.isInitialized()); + builder.setA(1); + assertFalse(abstractBuilder.isInitialized()); + builder.setB(1); + assertFalse(abstractBuilder.isInitialized()); + builder.setC(1); + assertTrue(abstractBuilder.isInitialized()); + } + + public void testForeignIsInitialized() throws Exception { + TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder(); + AbstractMessageWrapper.Builder abstractBuilder = + new AbstractMessageWrapper.Builder(builder); + + assertTrue(abstractBuilder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(abstractBuilder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED); + assertTrue(abstractBuilder.isInitialized()); + + builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(abstractBuilder.isInitialized()); + + builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED); + assertTrue(abstractBuilder.isInitialized()); + } + + // ----------------------------------------------------------------- + // Tests for mergeFrom + + static final TestAllTypes MERGE_SOURCE = + TestAllTypes.newBuilder() + .setOptionalInt32(1) + .setOptionalString("foo") + .setOptionalForeignMessage(ForeignMessage.getDefaultInstance()) + .addRepeatedString("bar") + .build(); + + static final TestAllTypes MERGE_DEST = + TestAllTypes.newBuilder() + .setOptionalInt64(2) + .setOptionalString("baz") + .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build()) + .addRepeatedString("qux") + .build(); + + static final String MERGE_RESULT_TEXT = + "optional_int32: 1\n" + + "optional_int64: 2\n" + + "optional_string: \"foo\"\n" + + "optional_foreign_message {\n" + + " c: 3\n" + + "}\n" + + "repeated_string: \"qux\"\n" + + "repeated_string: \"bar\"\n"; + + public void testMergeFrom() throws Exception { + AbstractMessageWrapper result = + new AbstractMessageWrapper.Builder( + TestAllTypes.newBuilder(MERGE_DEST)) + .mergeFrom(MERGE_SOURCE).build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + // ----------------------------------------------------------------- + // Tests for equals and hashCode + + public void testEqualsAndHashCode() { + TestAllTypes a = TestUtil.getAllSet(); + TestAllTypes b = TestAllTypes.newBuilder().build(); + TestAllTypes c = TestAllTypes.newBuilder(b).addRepeatedString("x").build(); + TestAllTypes d = TestAllTypes.newBuilder(c).addRepeatedString("y").build(); + TestAllExtensions e = TestUtil.getAllExtensionsSet(); + TestAllExtensions f = TestAllExtensions.newBuilder(e) + .addExtension(UnittestProto.repeatedInt32Extension, 999).build(); + + checkEqualsIsConsistent(a); + checkEqualsIsConsistent(b); + checkEqualsIsConsistent(c); + checkEqualsIsConsistent(d); + checkEqualsIsConsistent(e); + checkEqualsIsConsistent(f); + + checkNotEqual(a, b); + checkNotEqual(a, c); + checkNotEqual(a, d); + checkNotEqual(a, e); + checkNotEqual(a, f); + + checkNotEqual(b, c); + checkNotEqual(b, d); + checkNotEqual(b, e); + checkNotEqual(b, f); + + checkNotEqual(c, d); + checkNotEqual(c, e); + checkNotEqual(c, f); + + checkNotEqual(d, e); + checkNotEqual(d, f); + + checkNotEqual(e, f); + } + + /** + * Asserts that the given protos are equal and have the same hash code. + */ + private void checkEqualsIsConsistent(Message message) { + // Object should be equal to itself. + assertEquals(message, message); + + // Object should be equal to a dynamic copy of itself. + DynamicMessage dynamic = DynamicMessage.newBuilder(message).build(); + assertEquals(message, dynamic); + assertEquals(dynamic, message); + assertEquals(dynamic.hashCode(), message.hashCode()); + } + + /** + * Asserts that the given protos are not equal and have different hash codes. + * + * @warning It's valid for non-equal objects to have the same hash code, so + * this test is stricter than it needs to be. However, this should happen + * relatively rarely. + */ + private void checkNotEqual(Message m1, Message m2) { + String equalsError = String.format("%s should not be equal to %s", m1, m2); + assertFalse(equalsError, m1.equals(m2)); + assertFalse(equalsError, m2.equals(m1)); + + assertFalse( + String.format("%s should have a different hash code from %s", m1, m2), + m1.hashCode() == m2.hashCode()); + } +} diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java new file mode 100644 index 00000000..b34e56f0 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java @@ -0,0 +1,401 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestRecursiveMessage; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; + +/** + * Unit test for {@link CodedInputStream}. + * + * @author kenton@google.com Kenton Varda + */ +public class CodedInputStreamTest extends TestCase { + /** + * Helper to construct a byte array from a bunch of bytes. The inputs are + * actually ints so that I can use hex notation and not get stupid errors + * about precision. + */ + private byte[] bytes(int... bytesAsInts) { + byte[] bytes = new byte[bytesAsInts.length]; + for (int i = 0; i < bytesAsInts.length; i++) { + bytes[i] = (byte) bytesAsInts[i]; + } + return bytes; + } + + /** + * An InputStream which limits the number of bytes it reads at a time. + * We use this to make sure that CodedInputStream doesn't screw up when + * reading in small blocks. + */ + private static final class SmallBlockInputStream extends FilterInputStream { + private final int blockSize; + + public SmallBlockInputStream(byte[] data, int blockSize) { + this(new ByteArrayInputStream(data), blockSize); + } + + public SmallBlockInputStream(InputStream in, int blockSize) { + super(in); + this.blockSize = blockSize; + } + + public int read(byte[] b) throws IOException { + return super.read(b, 0, Math.min(b.length, blockSize)); + } + + public int read(byte[] b, int off, int len) throws IOException { + return super.read(b, off, Math.min(len, blockSize)); + } + } + + /** + * Parses the given bytes using readRawVarint32() and readRawVarint64() and + * checks that the result matches the given value. + */ + private void assertReadVarint(byte[] data, long value) throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + assertEquals((int)value, input.readRawVarint32()); + + input = CodedInputStream.newInstance(data); + assertEquals(value, input.readRawVarint64()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals((int)value, input.readRawVarint32()); + + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals(value, input.readRawVarint64()); + } + } + + /** + * Parses the given bytes using readRawVarint32() and readRawVarint64() and + * expects them to fail with an InvalidProtocolBufferException whose + * description matches the given one. + */ + private void assertReadVarintFailure( + InvalidProtocolBufferException expected, byte[] data) + throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + try { + input.readRawVarint32(); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals(expected.getMessage(), e.getMessage()); + } + + input = CodedInputStream.newInstance(data); + try { + input.readRawVarint64(); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals(expected.getMessage(), e.getMessage()); + } + } + + /** Tests readRawVarint32() and readRawVarint64(). */ + public void testReadVarint() throws Exception { + assertReadVarint(bytes(0x00), 0); + assertReadVarint(bytes(0x01), 1); + assertReadVarint(bytes(0x7f), 127); + // 14882 + assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7)); + // 2961488830 + assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x0bL << 28)); + + // 64-bit + // 7256456126 + assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x1bL << 28)); + // 41256202580718336 + assertReadVarint( + bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49), + (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | + (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49)); + // 11964378330978735131 + assertReadVarint( + bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), + (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | + (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) | + (0x05L << 49) | (0x26L << 56) | (0x01L << 63)); + + // Failures + assertReadVarintFailure( + InvalidProtocolBufferException.malformedVarint(), + bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00)); + assertReadVarintFailure( + InvalidProtocolBufferException.truncatedMessage(), + bytes(0x80)); + } + + /** + * Parses the given bytes using readRawLittleEndian32() and checks + * that the result matches the given value. + */ + private void assertReadLittleEndian32(byte[] data, int value) + throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + assertEquals(value, input.readRawLittleEndian32()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals(value, input.readRawLittleEndian32()); + } + } + + /** + * Parses the given bytes using readRawLittleEndian64() and checks + * that the result matches the given value. + */ + private void assertReadLittleEndian64(byte[] data, long value) + throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + assertEquals(value, input.readRawLittleEndian64()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals(value, input.readRawLittleEndian64()); + } + } + + /** Tests readRawLittleEndian32() and readRawLittleEndian64(). */ + public void testReadLittleEndian() throws Exception { + assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678); + assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0); + + assertReadLittleEndian64( + bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), + 0x123456789abcdef0L); + assertReadLittleEndian64( + bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), + 0x9abcdef012345678L); + } + + /** Test decodeZigZag32() and decodeZigZag64(). */ + public void testDecodeZigZag() throws Exception { + assertEquals( 0, CodedInputStream.decodeZigZag32(0)); + assertEquals(-1, CodedInputStream.decodeZigZag32(1)); + assertEquals( 1, CodedInputStream.decodeZigZag32(2)); + assertEquals(-2, CodedInputStream.decodeZigZag32(3)); + assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE)); + assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF)); + assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE)); + assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF)); + + assertEquals( 0, CodedInputStream.decodeZigZag64(0)); + assertEquals(-1, CodedInputStream.decodeZigZag64(1)); + assertEquals( 1, CodedInputStream.decodeZigZag64(2)); + assertEquals(-2, CodedInputStream.decodeZigZag64(3)); + assertEquals(0x000000003FFFFFFFL, + CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL)); + assertEquals(0xFFFFFFFFC0000000L, + CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL)); + assertEquals(0x000000007FFFFFFFL, + CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL)); + assertEquals(0xFFFFFFFF80000000L, + CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL)); + assertEquals(0x7FFFFFFFFFFFFFFFL, + CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL)); + assertEquals(0x8000000000000000L, + CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL)); + } + + /** Tests reading and parsing a whole message with every field type. */ + public void testReadWholeMessage() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + byte[] rawBytes = message.toByteArray(); + assertEquals(rawBytes.length, message.getSerializedSize()); + + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + TestUtil.assertAllFieldsSet(message2); + + // Try different block sizes. + for (int blockSize = 1; blockSize < 256; blockSize *= 2) { + message2 = TestAllTypes.parseFrom( + new SmallBlockInputStream(rawBytes, blockSize)); + TestUtil.assertAllFieldsSet(message2); + } + } + + /** Tests skipField(). */ + public void testSkipWholeMessage() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + byte[] rawBytes = message.toByteArray(); + + // Create two parallel inputs. Parse one as unknown fields while using + // skipField() to skip each field on the other. Expect the same tags. + CodedInputStream input1 = CodedInputStream.newInstance(rawBytes); + CodedInputStream input2 = CodedInputStream.newInstance(rawBytes); + UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder(); + + while (true) { + int tag = input1.readTag(); + assertEquals(tag, input2.readTag()); + if (tag == 0) { + break; + } + unknownFields.mergeFieldFrom(tag, input1); + input2.skipField(tag); + } + } + + public void testReadHugeBlob() throws Exception { + // Allocate and initialize a 1MB blob. + byte[] blob = new byte[1 << 20]; + for (int i = 0; i < blob.length; i++) { + blob[i] = (byte)i; + } + + // Make a message containing it. + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + builder.setOptionalBytes(ByteString.copyFrom(blob)); + TestAllTypes message = builder.build(); + + // Serialize and parse it. Make sure to parse from an InputStream, not + // directly from a ByteString, so that CodedInputStream uses buffered + // reading. + TestAllTypes message2 = + TestAllTypes.parseFrom(message.toByteString().newInput()); + + assertEquals(message.getOptionalBytes(), message2.getOptionalBytes()); + + // Make sure all the other fields were parsed correctly. + TestAllTypes message3 = TestAllTypes.newBuilder(message2) + .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes()) + .build(); + TestUtil.assertAllFieldsSet(message3); + } + + public void testReadMaliciouslyLargeBlob() throws Exception { + ByteString.Output rawOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + + int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(tag); + output.writeRawVarint32(0x7FFFFFFF); + output.writeRawBytes(new byte[32]); // Pad with a few random bytes. + output.flush(); + + CodedInputStream input = rawOutput.toByteString().newCodedInput(); + assertEquals(tag, input.readTag()); + + try { + input.readBytes(); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + } + + private TestRecursiveMessage makeRecursiveMessage(int depth) { + if (depth == 0) { + return TestRecursiveMessage.newBuilder().setI(5).build(); + } else { + return TestRecursiveMessage.newBuilder() + .setA(makeRecursiveMessage(depth - 1)).build(); + } + } + + private void assertMessageDepth(TestRecursiveMessage message, int depth) { + if (depth == 0) { + assertFalse(message.hasA()); + assertEquals(5, message.getI()); + } else { + assertTrue(message.hasA()); + assertMessageDepth(message.getA(), depth - 1); + } + } + + public void testMaliciousRecursion() throws Exception { + ByteString data64 = makeRecursiveMessage(64).toByteString(); + ByteString data65 = makeRecursiveMessage(65).toByteString(); + + assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64); + + try { + TestRecursiveMessage.parseFrom(data65); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + + CodedInputStream input = data64.newCodedInput(); + input.setRecursionLimit(8); + try { + TestRecursiveMessage.parseFrom(input); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + } + + public void testSizeLimit() throws Exception { + CodedInputStream input = CodedInputStream.newInstance( + TestUtil.getAllSet().toByteString().newInput()); + input.setSizeLimit(16); + + try { + TestAllTypes.parseFrom(input); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + } + + /** + * Tests that if we read an string that contains invalid UTF-8, no exception + * is thrown. Instead, the invalid bytes are replaced with the Unicode + * "replacement character" U+FFFD. + */ + public void testReadInvalidUtf8() throws Exception { + ByteString.Output rawOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + + int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(tag); + output.writeRawVarint32(1); + output.writeRawBytes(new byte[] { (byte)0x80 }); + output.flush(); + + CodedInputStream input = rawOutput.toByteString().newCodedInput(); + assertEquals(tag, input.readTag()); + String text = input.readString(); + assertEquals(0xfffd, text.charAt(0)); + } +} diff --git a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java new file mode 100644 index 00000000..7ee75534 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java @@ -0,0 +1,280 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Unit test for {@link CodedOutputStream}. + * + * @author kenton@google.com Kenton Varda + */ +public class CodedOutputStreamTest extends TestCase { + /** + * Helper to construct a byte array from a bunch of bytes. The inputs are + * actually ints so that I can use hex notation and not get stupid errors + * about precision. + */ + private byte[] bytes(int... bytesAsInts) { + byte[] bytes = new byte[bytesAsInts.length]; + for (int i = 0; i < bytesAsInts.length; i++) { + bytes[i] = (byte) bytesAsInts[i]; + } + return bytes; + } + + /** Arrays.asList() does not work with arrays of primitives. :( */ + private List toList(byte[] bytes) { + List result = new ArrayList(); + for (byte b : bytes) { + result.add(b); + } + return result; + } + + private void assertEqualBytes(byte[] a, byte[] b) { + assertEquals(toList(a), toList(b)); + } + + /** + * Writes the given value using writeRawVarint32() and writeRawVarint64() and + * checks that the result matches the given bytes. + */ + private void assertWriteVarint(byte[] data, long value) throws Exception { + // Only do 32-bit write if the value fits in 32 bits. + if ((value >>> 32) == 0) { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawVarint32((int) value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Also try computing size. + assertEquals(data.length, + CodedOutputStream.computeRawVarint32Size((int) value)); + } + + { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawVarint64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Also try computing size. + assertEquals(data.length, + CodedOutputStream.computeRawVarint64Size(value)); + } + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + // Only do 32-bit write if the value fits in 32 bits. + if ((value >>> 32) == 0) { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = + CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawVarint32((int) value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + + { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = + CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawVarint64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + } + } + + /** Tests writeRawVarint32() and writeRawVarint64(). */ + public void testWriteVarint() throws Exception { + assertWriteVarint(bytes(0x00), 0); + assertWriteVarint(bytes(0x01), 1); + assertWriteVarint(bytes(0x7f), 127); + // 14882 + assertWriteVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7)); + // 2961488830 + assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x0bL << 28)); + + // 64-bit + // 7256456126 + assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x1bL << 28)); + // 41256202580718336 + assertWriteVarint( + bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49), + (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | + (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49)); + // 11964378330978735131 + assertWriteVarint( + bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), + (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | + (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) | + (0x05L << 49) | (0x26L << 56) | (0x01L << 63)); + } + + /** + * Parses the given bytes using writeRawLittleEndian32() and checks + * that the result matches the given value. + */ + private void assertWriteLittleEndian32(byte[] data, int value) + throws Exception { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawLittleEndian32(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + rawOutput = new ByteArrayOutputStream(); + output = CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawLittleEndian32(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + } + + /** + * Parses the given bytes using writeRawLittleEndian64() and checks + * that the result matches the given value. + */ + private void assertWriteLittleEndian64(byte[] data, long value) + throws Exception { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawLittleEndian64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + rawOutput = new ByteArrayOutputStream(); + output = CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawLittleEndian64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + } + + /** Tests writeRawLittleEndian32() and writeRawLittleEndian64(). */ + public void testWriteLittleEndian() throws Exception { + assertWriteLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678); + assertWriteLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0); + + assertWriteLittleEndian64( + bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), + 0x123456789abcdef0L); + assertWriteLittleEndian64( + bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), + 0x9abcdef012345678L); + } + + /** Test encodeZigZag32() and encodeZigZag64(). */ + public void testEncodeZigZag() throws Exception { + assertEquals(0, CodedOutputStream.encodeZigZag32( 0)); + assertEquals(1, CodedOutputStream.encodeZigZag32(-1)); + assertEquals(2, CodedOutputStream.encodeZigZag32( 1)); + assertEquals(3, CodedOutputStream.encodeZigZag32(-2)); + assertEquals(0x7FFFFFFE, CodedOutputStream.encodeZigZag32(0x3FFFFFFF)); + assertEquals(0x7FFFFFFF, CodedOutputStream.encodeZigZag32(0xC0000000)); + assertEquals(0xFFFFFFFE, CodedOutputStream.encodeZigZag32(0x7FFFFFFF)); + assertEquals(0xFFFFFFFF, CodedOutputStream.encodeZigZag32(0x80000000)); + + assertEquals(0, CodedOutputStream.encodeZigZag64( 0)); + assertEquals(1, CodedOutputStream.encodeZigZag64(-1)); + assertEquals(2, CodedOutputStream.encodeZigZag64( 1)); + assertEquals(3, CodedOutputStream.encodeZigZag64(-2)); + assertEquals(0x000000007FFFFFFEL, + CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL)); + assertEquals(0x000000007FFFFFFFL, + CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L)); + assertEquals(0x00000000FFFFFFFEL, + CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL)); + assertEquals(0x00000000FFFFFFFFL, + CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L)); + assertEquals(0xFFFFFFFFFFFFFFFEL, + CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL)); + assertEquals(0xFFFFFFFFFFFFFFFFL, + CodedOutputStream.encodeZigZag64(0x8000000000000000L)); + + // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1) + // were chosen semi-randomly via keyboard bashing. + assertEquals(0, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0))); + assertEquals(1, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1))); + assertEquals(-1, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1))); + assertEquals(14927, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927))); + assertEquals(-3612, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612))); + + assertEquals(0, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0))); + assertEquals(1, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1))); + assertEquals(-1, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1))); + assertEquals(14927, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927))); + assertEquals(-3612, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612))); + + assertEquals(856912304801416L, + CodedOutputStream.encodeZigZag64( + CodedInputStream.decodeZigZag64( + 856912304801416L))); + assertEquals(-75123905439571256L, + CodedOutputStream.encodeZigZag64( + CodedInputStream.decodeZigZag64( + -75123905439571256L))); + } + + /** Tests writing a whole message with every field type. */ + public void testWriteWholeMessage() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + byte[] rawBytes = message.toByteArray(); + assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes); + + // Try different block sizes. + for (int blockSize = 1; blockSize < 256; blockSize *= 2) { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = + CodedOutputStream.newInstance(rawOutput, blockSize); + message.writeTo(output); + output.flush(); + assertEqualBytes(rawBytes, rawOutput.toByteArray()); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java new file mode 100644 index 00000000..98d6d01a --- /dev/null +++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -0,0 +1,313 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; +import com.google.protobuf.Descriptors.ServiceDescriptor; +import com.google.protobuf.Descriptors.MethodDescriptor; + +import com.google.protobuf.test.UnittestImport; +import com.google.protobuf.test.UnittestImport.ImportEnum; +import com.google.protobuf.test.UnittestImport.ImportMessage; +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; +import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestService; + + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.Collections; + +/** + * Unit test for {@link Descriptors}. + * + * @author kenton@google.com Kenton Varda + */ +public class DescriptorsTest extends TestCase { + public void testFileDescriptor() throws Exception { + FileDescriptor file = UnittestProto.getDescriptor(); + + assertEquals("google/protobuf/unittest.proto", file.getName()); + assertEquals("protobuf_unittest", file.getPackage()); + + assertEquals("UnittestProto", file.getOptions().getJavaOuterClassname()); + assertEquals("google/protobuf/unittest.proto", + file.toProto().getName()); + + assertEquals(Arrays.asList(UnittestImport.getDescriptor()), + file.getDependencies()); + + Descriptor messageType = TestAllTypes.getDescriptor(); + assertEquals(messageType, file.getMessageTypes().get(0)); + assertEquals(messageType, file.findMessageTypeByName("TestAllTypes")); + assertNull(file.findMessageTypeByName("NoSuchType")); + assertNull(file.findMessageTypeByName("protobuf_unittest.TestAllTypes")); + for (int i = 0; i < file.getMessageTypes().size(); i++) { + assertEquals(i, file.getMessageTypes().get(i).getIndex()); + } + + EnumDescriptor enumType = ForeignEnum.getDescriptor(); + assertEquals(enumType, file.getEnumTypes().get(0)); + assertEquals(enumType, file.findEnumTypeByName("ForeignEnum")); + assertNull(file.findEnumTypeByName("NoSuchType")); + assertNull(file.findEnumTypeByName("protobuf_unittest.ForeignEnum")); + assertEquals(Arrays.asList(ImportEnum.getDescriptor()), + UnittestImport.getDescriptor().getEnumTypes()); + for (int i = 0; i < file.getEnumTypes().size(); i++) { + assertEquals(i, file.getEnumTypes().get(i).getIndex()); + } + + ServiceDescriptor service = TestService.getDescriptor(); + assertEquals(service, file.getServices().get(0)); + assertEquals(service, file.findServiceByName("TestService")); + assertNull(file.findServiceByName("NoSuchType")); + assertNull(file.findServiceByName("protobuf_unittest.TestService")); + assertEquals(Collections.emptyList(), + UnittestImport.getDescriptor().getServices()); + for (int i = 0; i < file.getServices().size(); i++) { + assertEquals(i, file.getServices().get(i).getIndex()); + } + + FieldDescriptor extension = + UnittestProto.optionalInt32Extension.getDescriptor(); + assertEquals(extension, file.getExtensions().get(0)); + assertEquals(extension, + file.findExtensionByName("optional_int32_extension")); + assertNull(file.findExtensionByName("no_such_ext")); + assertNull(file.findExtensionByName( + "protobuf_unittest.optional_int32_extension")); + assertEquals(Collections.emptyList(), + UnittestImport.getDescriptor().getExtensions()); + for (int i = 0; i < file.getExtensions().size(); i++) { + assertEquals(i, file.getExtensions().get(i).getIndex()); + } + } + + public void testDescriptor() throws Exception { + Descriptor messageType = TestAllTypes.getDescriptor(); + Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor(); + + assertEquals("TestAllTypes", messageType.getName()); + assertEquals("protobuf_unittest.TestAllTypes", messageType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), messageType.getFile()); + assertNull(messageType.getContainingType()); + assertEquals(DescriptorProtos.MessageOptions.getDefaultInstance(), + messageType.getOptions()); + assertEquals("TestAllTypes", messageType.toProto().getName()); + + assertEquals("NestedMessage", nestedType.getName()); + assertEquals("protobuf_unittest.TestAllTypes.NestedMessage", + nestedType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), nestedType.getFile()); + assertEquals(messageType, nestedType.getContainingType()); + + FieldDescriptor field = messageType.getFields().get(0); + assertEquals("optional_int32", field.getName()); + assertEquals(field, messageType.findFieldByName("optional_int32")); + assertNull(messageType.findFieldByName("no_such_field")); + assertEquals(field, messageType.findFieldByNumber(1)); + assertNull(messageType.findFieldByNumber(571283)); + for (int i = 0; i < messageType.getFields().size(); i++) { + assertEquals(i, messageType.getFields().get(i).getIndex()); + } + + assertEquals(nestedType, messageType.getNestedTypes().get(0)); + assertEquals(nestedType, messageType.findNestedTypeByName("NestedMessage")); + assertNull(messageType.findNestedTypeByName("NoSuchType")); + for (int i = 0; i < messageType.getNestedTypes().size(); i++) { + assertEquals(i, messageType.getNestedTypes().get(i).getIndex()); + } + + EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); + assertEquals(enumType, messageType.getEnumTypes().get(0)); + assertEquals(enumType, messageType.findEnumTypeByName("NestedEnum")); + assertNull(messageType.findEnumTypeByName("NoSuchType")); + for (int i = 0; i < messageType.getEnumTypes().size(); i++) { + assertEquals(i, messageType.getEnumTypes().get(i).getIndex()); + } + } + + public void testFieldDescriptor() throws Exception { + Descriptor messageType = TestAllTypes.getDescriptor(); + FieldDescriptor primitiveField = + messageType.findFieldByName("optional_int32"); + FieldDescriptor enumField = + messageType.findFieldByName("optional_nested_enum"); + FieldDescriptor messageField = + messageType.findFieldByName("optional_foreign_message"); + FieldDescriptor cordField = + messageType.findFieldByName("optional_cord"); + FieldDescriptor extension = + UnittestProto.optionalInt32Extension.getDescriptor(); + FieldDescriptor nestedExtension = TestRequired.single.getDescriptor(); + + assertEquals("optional_int32", primitiveField.getName()); + assertEquals("protobuf_unittest.TestAllTypes.optional_int32", + primitiveField.getFullName()); + assertEquals(1, primitiveField.getNumber()); + assertEquals(messageType, primitiveField.getContainingType()); + assertEquals(UnittestProto.getDescriptor(), primitiveField.getFile()); + assertEquals(FieldDescriptor.Type.INT32, primitiveField.getType()); + assertEquals(FieldDescriptor.JavaType.INT, primitiveField.getJavaType()); + assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(), + primitiveField.getOptions()); + assertFalse(primitiveField.isExtension()); + assertEquals("optional_int32", primitiveField.toProto().getName()); + + assertEquals("optional_nested_enum", enumField.getName()); + assertEquals(FieldDescriptor.Type.ENUM, enumField.getType()); + assertEquals(FieldDescriptor.JavaType.ENUM, enumField.getJavaType()); + assertEquals(TestAllTypes.NestedEnum.getDescriptor(), + enumField.getEnumType()); + + assertEquals("optional_foreign_message", messageField.getName()); + assertEquals(FieldDescriptor.Type.MESSAGE, messageField.getType()); + assertEquals(FieldDescriptor.JavaType.MESSAGE, messageField.getJavaType()); + assertEquals(ForeignMessage.getDescriptor(), messageField.getMessageType()); + + assertEquals("optional_cord", cordField.getName()); + assertEquals(FieldDescriptor.Type.STRING, cordField.getType()); + assertEquals(FieldDescriptor.JavaType.STRING, cordField.getJavaType()); + assertEquals(DescriptorProtos.FieldOptions.CType.CORD, + cordField.getOptions().getCtype()); + + assertEquals("optional_int32_extension", extension.getName()); + assertEquals("protobuf_unittest.optional_int32_extension", + extension.getFullName()); + assertEquals(1, extension.getNumber()); + assertEquals(TestAllExtensions.getDescriptor(), + extension.getContainingType()); + assertEquals(UnittestProto.getDescriptor(), extension.getFile()); + assertEquals(FieldDescriptor.Type.INT32, extension.getType()); + assertEquals(FieldDescriptor.JavaType.INT, extension.getJavaType()); + assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(), + extension.getOptions()); + assertTrue(extension.isExtension()); + assertEquals(null, extension.getExtensionScope()); + assertEquals("optional_int32_extension", extension.toProto().getName()); + + assertEquals("single", nestedExtension.getName()); + assertEquals("protobuf_unittest.TestRequired.single", + nestedExtension.getFullName()); + assertEquals(TestRequired.getDescriptor(), + nestedExtension.getExtensionScope()); + } + + public void testFieldDescriptorLabel() throws Exception { + FieldDescriptor requiredField = + TestRequired.getDescriptor().findFieldByName("a"); + FieldDescriptor optionalField = + TestAllTypes.getDescriptor().findFieldByName("optional_int32"); + FieldDescriptor repeatedField = + TestAllTypes.getDescriptor().findFieldByName("repeated_int32"); + + assertTrue(requiredField.isRequired()); + assertFalse(requiredField.isRepeated()); + assertFalse(optionalField.isRequired()); + assertFalse(optionalField.isRepeated()); + assertFalse(repeatedField.isRequired()); + assertTrue(repeatedField.isRepeated()); + } + + public void testFieldDescriptorDefault() throws Exception { + Descriptor d = TestAllTypes.getDescriptor(); + assertFalse(d.findFieldByName("optional_int32").hasDefaultValue()); + assertEquals(0, d.findFieldByName("optional_int32").getDefaultValue()); + assertTrue(d.findFieldByName("default_int32").hasDefaultValue()); + assertEquals(41, d.findFieldByName("default_int32").getDefaultValue()); + + d = TestExtremeDefaultValues.getDescriptor(); + assertEquals( + ByteString.copyFrom( + "\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes("ISO-8859-1")), + d.findFieldByName("escaped_bytes").getDefaultValue()); + assertEquals(-1, d.findFieldByName("large_uint32").getDefaultValue()); + assertEquals(-1L, d.findFieldByName("large_uint64").getDefaultValue()); + } + + public void testEnumDescriptor() throws Exception { + EnumDescriptor enumType = ForeignEnum.getDescriptor(); + EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor(); + + assertEquals("ForeignEnum", enumType.getName()); + assertEquals("protobuf_unittest.ForeignEnum", enumType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), enumType.getFile()); + assertNull(enumType.getContainingType()); + assertEquals(DescriptorProtos.EnumOptions.getDefaultInstance(), + enumType.getOptions()); + + assertEquals("NestedEnum", nestedType.getName()); + assertEquals("protobuf_unittest.TestAllTypes.NestedEnum", + nestedType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), nestedType.getFile()); + assertEquals(TestAllTypes.getDescriptor(), nestedType.getContainingType()); + + EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor(); + assertEquals(value, enumType.getValues().get(0)); + assertEquals("FOREIGN_FOO", value.getName()); + assertEquals(4, value.getNumber()); + assertEquals(value, enumType.findValueByName("FOREIGN_FOO")); + assertEquals(value, enumType.findValueByNumber(4)); + assertNull(enumType.findValueByName("NO_SUCH_VALUE")); + for (int i = 0; i < enumType.getValues().size(); i++) { + assertEquals(i, enumType.getValues().get(i).getIndex()); + } + } + + public void testServiceDescriptor() throws Exception { + ServiceDescriptor service = TestService.getDescriptor(); + + assertEquals("TestService", service.getName()); + assertEquals("protobuf_unittest.TestService", service.getFullName()); + assertEquals(UnittestProto.getDescriptor(), service.getFile()); + + assertEquals(2, service.getMethods().size()); + + MethodDescriptor fooMethod = service.getMethods().get(0); + assertEquals("Foo", fooMethod.getName()); + assertEquals(UnittestProto.FooRequest.getDescriptor(), + fooMethod.getInputType()); + assertEquals(UnittestProto.FooResponse.getDescriptor(), + fooMethod.getOutputType()); + assertEquals(fooMethod, service.findMethodByName("Foo")); + + MethodDescriptor barMethod = service.getMethods().get(1); + assertEquals("Bar", barMethod.getName()); + assertEquals(UnittestProto.BarRequest.getDescriptor(), + barMethod.getInputType()); + assertEquals(UnittestProto.BarResponse.getDescriptor(), + barMethod.getOutputType()); + assertEquals(barMethod, service.findMethodByName("Bar")); + + assertNull(service.findMethodByName("NoSuchMethod")); + + for (int i = 0; i < service.getMethods().size(); i++) { + assertEquals(i, service.getMethods().get(i).getIndex()); + } + } + +} diff --git a/java/src/test/java/com/google/protobuf/DynamicMessageTest.java b/java/src/test/java/com/google/protobuf/DynamicMessageTest.java new file mode 100644 index 00000000..7a458981 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/DynamicMessageTest.java @@ -0,0 +1,120 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; + +import junit.framework.TestCase; + +/** + * Unit test for {@link DynamicMessage}. See also {@link MessageTest}, which + * tests some {@link DynamicMessage} functionality. + * + * @author kenton@google.com Kenton Varda + */ +public class DynamicMessageTest extends TestCase { + TestUtil.ReflectionTester reflectionTester = + new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null); + + TestUtil.ReflectionTester extensionsReflectionTester = + new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), + TestUtil.getExtensionRegistry()); + + public void testDynamicMessageAccessors() throws Exception { + Message.Builder builder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(builder); + Message message = builder.build(); + reflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testDynamicMessageExtensionAccessors() throws Exception { + // We don't need to extensively test DynamicMessage's handling of + // extensions because, frankly, it doesn't do anything special with them. + // It treats them just like any other fields. + Message.Builder builder = + DynamicMessage.newBuilder(TestAllExtensions.getDescriptor()); + extensionsReflectionTester.setAllFieldsViaReflection(builder); + Message message = builder.build(); + extensionsReflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testDynamicMessageRepeatedSetters() throws Exception { + Message.Builder builder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(builder); + reflectionTester.modifyRepeatedFieldsViaReflection(builder); + Message message = builder.build(); + reflectionTester.assertRepeatedFieldsModifiedViaReflection(message); + } + + public void testDynamicMessageDefaults() throws Exception { + reflectionTester.assertClearViaReflection( + DynamicMessage.getDefaultInstance(TestAllTypes.getDescriptor())); + reflectionTester.assertClearViaReflection( + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()).build()); + } + + public void testDynamicMessageSerializedSize() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + Message.Builder dynamicBuilder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(dynamicBuilder); + Message dynamicMessage = dynamicBuilder.build(); + + assertEquals(message.getSerializedSize(), + dynamicMessage.getSerializedSize()); + } + + public void testDynamicMessageSerialization() throws Exception { + Message.Builder builder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(builder); + Message message = builder.build(); + + ByteString rawBytes = message.toByteString(); + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + + TestUtil.assertAllFieldsSet(message2); + + // In fact, the serialized forms should be exactly the same, byte-for-byte. + assertEquals(TestUtil.getAllSet().toByteString(), rawBytes); + } + + public void testDynamicMessageParsing() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + + ByteString rawBytes = message.toByteString(); + + Message message2 = + DynamicMessage.parseFrom(TestAllTypes.getDescriptor(), rawBytes); + reflectionTester.assertAllFieldsSetViaReflection(message2); + } + + public void testDynamicMessageCopy() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + + DynamicMessage copy = DynamicMessage.newBuilder(message).build(); + reflectionTester.assertAllFieldsSetViaReflection(copy); + } +} diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java new file mode 100644 index 00000000..30d73d28 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -0,0 +1,246 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; +import protobuf_unittest.MultipleFilesTestProto; +import protobuf_unittest.MessageWithNoOuter; +import protobuf_unittest.EnumWithNoOuter; +import protobuf_unittest.ServiceWithNoOuter; + +import junit.framework.TestCase; +import java.util.Arrays; + +/** + * Unit test for generated messages and generated code. See also + * {@link MessageTest}, which tests some generated message functionality. + * + * @author kenton@google.com Kenton Varda + */ +public class GeneratedMessageTest extends TestCase { + TestUtil.ReflectionTester reflectionTester = + new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null); + + public void testDefaultInstance() throws Exception { + assertSame(TestAllTypes.getDefaultInstance(), + TestAllTypes.getDefaultInstance().getDefaultInstanceForType()); + assertSame(TestAllTypes.getDefaultInstance(), + TestAllTypes.newBuilder().getDefaultInstanceForType()); + } + + public void testAccessors() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + TestUtil.assertAllFieldsSet(message); + } + + public void testRepeatedSetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestUtil.modifyRepeatedFields(builder); + TestAllTypes message = builder.build(); + TestUtil.assertRepeatedFieldsModified(message); + } + + public void testRepeatedAppend() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + + builder.addAllRepeatedInt32(Arrays.asList(1, 2, 3, 4)); + builder.addAllRepeatedForeignEnum(Arrays.asList(ForeignEnum.FOREIGN_BAZ)); + + ForeignMessage foreignMessage = + ForeignMessage.newBuilder().setC(12).build(); + builder.addAllRepeatedForeignMessage(Arrays.asList(foreignMessage)); + + TestAllTypes message = builder.build(); + assertEquals(message.getRepeatedInt32List(), Arrays.asList(1, 2, 3, 4)); + assertEquals(message.getRepeatedForeignEnumList(), + Arrays.asList(ForeignEnum.FOREIGN_BAZ)); + assertEquals(1, message.getRepeatedForeignMessageCount()); + assertEquals(12, message.getRepeatedForeignMessage(0).getC()); + } + + public void testSettingForeignMessageUsingBuilder() throws Exception { + TestAllTypes message = TestAllTypes.newBuilder() + // Pass builder for foreign message instance. + .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(123)) + .build(); + TestAllTypes expectedMessage = TestAllTypes.newBuilder() + // Create expected version passing foreign message instance explicitly. + .setOptionalForeignMessage( + ForeignMessage.newBuilder().setC(123).build()) + .build(); + // TODO(ngd): Upgrade to using real #equals method once implemented + assertEquals(expectedMessage.toString(), message.toString()); + } + + public void testSettingRepeatedForeignMessageUsingBuilder() throws Exception { + TestAllTypes message = TestAllTypes.newBuilder() + // Pass builder for foreign message instance. + .addRepeatedForeignMessage(ForeignMessage.newBuilder().setC(456)) + .build(); + TestAllTypes expectedMessage = TestAllTypes.newBuilder() + // Create expected version passing foreign message instance explicitly. + .addRepeatedForeignMessage( + ForeignMessage.newBuilder().setC(456).build()) + .build(); + assertEquals(expectedMessage.toString(), message.toString()); + } + + public void testDefaults() throws Exception { + TestUtil.assertClear(TestAllTypes.getDefaultInstance()); + TestUtil.assertClear(TestAllTypes.newBuilder().build()); + + assertEquals("\u1234", + TestExtremeDefaultValues.getDefaultInstance().getUtf8String()); + } + + public void testReflectionGetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + reflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testReflectionSetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + reflectionTester.setAllFieldsViaReflection(builder); + TestAllTypes message = builder.build(); + TestUtil.assertAllFieldsSet(message); + } + + public void testReflectionRepeatedSetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + reflectionTester.setAllFieldsViaReflection(builder); + reflectionTester.modifyRepeatedFieldsViaReflection(builder); + TestAllTypes message = builder.build(); + TestUtil.assertRepeatedFieldsModified(message); + } + + public void testReflectionDefaults() throws Exception { + reflectionTester.assertClearViaReflection( + TestAllTypes.getDefaultInstance()); + reflectionTester.assertClearViaReflection( + TestAllTypes.newBuilder().build()); + } + + // ================================================================= + // Extensions. + + TestUtil.ReflectionTester extensionsReflectionTester = + new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), + TestUtil.getExtensionRegistry()); + + public void testExtensionAccessors() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TestUtil.setAllExtensions(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertAllExtensionsSet(message); + } + + public void testExtensionRepeatedSetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TestUtil.setAllExtensions(builder); + TestUtil.modifyRepeatedExtensions(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertRepeatedExtensionsModified(message); + } + + public void testExtensionDefaults() throws Exception { + TestUtil.assertExtensionsClear(TestAllExtensions.getDefaultInstance()); + TestUtil.assertExtensionsClear(TestAllExtensions.newBuilder().build()); + } + + public void testExtensionReflectionGetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TestUtil.setAllExtensions(builder); + TestAllExtensions message = builder.build(); + extensionsReflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testExtensionReflectionSetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + extensionsReflectionTester.setAllFieldsViaReflection(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertAllExtensionsSet(message); + } + + public void testExtensionReflectionRepeatedSetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + extensionsReflectionTester.setAllFieldsViaReflection(builder); + extensionsReflectionTester.modifyRepeatedFieldsViaReflection(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertRepeatedExtensionsModified(message); + } + + public void testExtensionReflectionDefaults() throws Exception { + extensionsReflectionTester.assertClearViaReflection( + TestAllExtensions.getDefaultInstance()); + extensionsReflectionTester.assertClearViaReflection( + TestAllExtensions.newBuilder().build()); + } + + public void testClearExtension() throws Exception { + // clearExtension() is not actually used in TestUtil, so try it manually. + assertFalse( + TestAllExtensions.newBuilder() + .setExtension(UnittestProto.optionalInt32Extension, 1) + .clearExtension(UnittestProto.optionalInt32Extension) + .hasExtension(UnittestProto.optionalInt32Extension)); + assertEquals(0, + TestAllExtensions.newBuilder() + .addExtension(UnittestProto.repeatedInt32Extension, 1) + .clearExtension(UnittestProto.repeatedInt32Extension) + .getExtensionCount(UnittestProto.repeatedInt32Extension)); + } + + // ================================================================= + // multiple_files_test + + public void testMultipleFilesOption() throws Exception { + // We mostly just want to check that things compile. + MessageWithNoOuter message = + MessageWithNoOuter.newBuilder() + .setNested(MessageWithNoOuter.NestedMessage.newBuilder().setI(1)) + .addForeign(TestAllTypes.newBuilder().setOptionalInt32(1)) + .setNestedEnum(MessageWithNoOuter.NestedEnum.BAZ) + .setForeignEnum(EnumWithNoOuter.BAR) + .build(); + assertEquals(message, MessageWithNoOuter.parseFrom(message.toByteString())); + + assertEquals(MultipleFilesTestProto.getDescriptor(), + MessageWithNoOuter.getDescriptor().getFile()); + + Descriptors.FieldDescriptor field = + MessageWithNoOuter.getDescriptor().findFieldByName("foreign_enum"); + assertEquals(EnumWithNoOuter.BAR.getValueDescriptor(), + message.getField(field)); + + assertEquals(MultipleFilesTestProto.getDescriptor(), + ServiceWithNoOuter.getDescriptor().getFile()); + + assertFalse( + TestAllExtensions.getDefaultInstance().hasExtension( + MultipleFilesTestProto.extensionWithOuter)); + } +} diff --git a/java/src/test/java/com/google/protobuf/MessageTest.java b/java/src/test/java/com/google/protobuf/MessageTest.java new file mode 100644 index 00000000..3dece1ff --- /dev/null +++ b/java/src/test/java/com/google/protobuf/MessageTest.java @@ -0,0 +1,299 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestRequiredForeign; +import protobuf_unittest.UnittestProto.ForeignMessage; + +import junit.framework.TestCase; + +/** + * Misc. unit tests for message operations that apply to both generated + * and dynamic messages. + * + * @author kenton@google.com Kenton Varda + */ +public class MessageTest extends TestCase { + // ================================================================= + // Message-merging tests. + + static final TestAllTypes MERGE_SOURCE = + TestAllTypes.newBuilder() + .setOptionalInt32(1) + .setOptionalString("foo") + .setOptionalForeignMessage(ForeignMessage.getDefaultInstance()) + .addRepeatedString("bar") + .build(); + + static final TestAllTypes MERGE_DEST = + TestAllTypes.newBuilder() + .setOptionalInt64(2) + .setOptionalString("baz") + .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build()) + .addRepeatedString("qux") + .build(); + + static final String MERGE_RESULT_TEXT = + "optional_int32: 1\n" + + "optional_int64: 2\n" + + "optional_string: \"foo\"\n" + + "optional_foreign_message {\n" + + " c: 3\n" + + "}\n" + + "repeated_string: \"qux\"\n" + + "repeated_string: \"bar\"\n"; + + public void testMergeFrom() throws Exception { + TestAllTypes result = + TestAllTypes.newBuilder(MERGE_DEST) + .mergeFrom(MERGE_SOURCE).build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + /** + * Test merging a DynamicMessage into a GeneratedMessage. As long as they + * have the same descriptor, this should work, but it is an entirely different + * code path. + */ + public void testMergeFromDynamic() throws Exception { + TestAllTypes result = + TestAllTypes.newBuilder(MERGE_DEST) + .mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build()) + .build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + /** Test merging two DynamicMessages. */ + public void testDynamicMergeFrom() throws Exception { + DynamicMessage result = + DynamicMessage.newBuilder(MERGE_DEST) + .mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build()) + .build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + // ================================================================= + // Required-field-related tests. + + private static final TestRequired TEST_REQUIRED_UNINITIALIZED = + TestRequired.getDefaultInstance(); + private static final TestRequired TEST_REQUIRED_INITIALIZED = + TestRequired.newBuilder().setA(1).setB(2).setC(3).build(); + + public void testRequired() throws Exception { + TestRequired.Builder builder = TestRequired.newBuilder(); + + assertFalse(builder.isInitialized()); + builder.setA(1); + assertFalse(builder.isInitialized()); + builder.setB(1); + assertFalse(builder.isInitialized()); + builder.setC(1); + assertTrue(builder.isInitialized()); + } + + public void testRequiredForeign() throws Exception { + TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder(); + + assertTrue(builder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + + builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + } + + public void testRequiredExtension() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + + assertTrue(builder.isInitialized()); + + builder.setExtension(TestRequired.single, TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setExtension(TestRequired.single, TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + + builder.addExtension(TestRequired.multi, TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setExtension(TestRequired.multi, 0, TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + } + + public void testRequiredDynamic() throws Exception { + Descriptors.Descriptor descriptor = TestRequired.getDescriptor(); + DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor); + + assertFalse(builder.isInitialized()); + builder.setField(descriptor.findFieldByName("a"), 1); + assertFalse(builder.isInitialized()); + builder.setField(descriptor.findFieldByName("b"), 1); + assertFalse(builder.isInitialized()); + builder.setField(descriptor.findFieldByName("c"), 1); + assertTrue(builder.isInitialized()); + } + + public void testRequiredDynamicForeign() throws Exception { + Descriptors.Descriptor descriptor = TestRequiredForeign.getDescriptor(); + DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor); + + assertTrue(builder.isInitialized()); + + builder.setField(descriptor.findFieldByName("optional_message"), + TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setField(descriptor.findFieldByName("optional_message"), + TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + + builder.addRepeatedField(descriptor.findFieldByName("repeated_message"), + TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setRepeatedField(descriptor.findFieldByName("repeated_message"), 0, + TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + } + + public void testUninitializedException() throws Exception { + try { + TestRequired.newBuilder().build(); + fail("Should have thrown an exception."); + } catch (UninitializedMessageException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } + + public void testBuildPartial() throws Exception { + // We're mostly testing that no exception is thrown. + TestRequired message = TestRequired.newBuilder().buildPartial(); + assertFalse(message.isInitialized()); + } + + public void testNestedUninitializedException() throws Exception { + try { + TestRequiredForeign.newBuilder() + .setOptionalMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .build(); + fail("Should have thrown an exception."); + } catch (UninitializedMessageException e) { + assertEquals( + "Message missing required fields: " + + "optional_message.a, " + + "optional_message.b, " + + "optional_message.c, " + + "repeated_message[0].a, " + + "repeated_message[0].b, " + + "repeated_message[0].c, " + + "repeated_message[1].a, " + + "repeated_message[1].b, " + + "repeated_message[1].c", + e.getMessage()); + } + } + + public void testBuildNestedPartial() throws Exception { + // We're mostly testing that no exception is thrown. + TestRequiredForeign message = + TestRequiredForeign.newBuilder() + .setOptionalMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .buildPartial(); + assertFalse(message.isInitialized()); + } + + public void testParseUnititialized() throws Exception { + try { + TestRequired.parseFrom(ByteString.EMPTY); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } + + public void testParseNestedUnititialized() throws Exception { + ByteString data = + TestRequiredForeign.newBuilder() + .setOptionalMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .buildPartial().toByteString(); + + try { + TestRequiredForeign.parseFrom(data); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals( + "Message missing required fields: " + + "optional_message.a, " + + "optional_message.b, " + + "optional_message.c, " + + "repeated_message[0].a, " + + "repeated_message[0].b, " + + "repeated_message[0].c, " + + "repeated_message[1].a, " + + "repeated_message[1].b, " + + "repeated_message[1].c", + e.getMessage()); + } + } + + public void testDynamicUninitializedException() throws Exception { + try { + DynamicMessage.newBuilder(TestRequired.getDescriptor()).build(); + fail("Should have thrown an exception."); + } catch (UninitializedMessageException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } + + public void testDynamicBuildPartial() throws Exception { + // We're mostly testing that no exception is thrown. + DynamicMessage message = + DynamicMessage.newBuilder(TestRequired.getDescriptor()) + .buildPartial(); + assertFalse(message.isInitialized()); + } + + public void testDynamicParseUnititialized() throws Exception { + try { + Descriptors.Descriptor descriptor = TestRequired.getDescriptor(); + DynamicMessage.parseFrom(descriptor, ByteString.EMPTY); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/ServiceTest.java b/java/src/test/java/com/google/protobuf/ServiceTest.java new file mode 100644 index 00000000..2f83837b --- /dev/null +++ b/java/src/test/java/com/google/protobuf/ServiceTest.java @@ -0,0 +1,164 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestService; +import protobuf_unittest.UnittestProto.FooRequest; +import protobuf_unittest.UnittestProto.FooResponse; +import protobuf_unittest.UnittestProto.BarRequest; +import protobuf_unittest.UnittestProto.BarResponse; + +import org.easymock.classextension.EasyMock; +import org.easymock.classextension.IMocksControl; +import org.easymock.IArgumentMatcher; + +import junit.framework.TestCase; + +/** + * Tests services and stubs. + * + * @author kenton@google.com Kenton Varda + */ +public class ServiceTest extends TestCase { + private IMocksControl control; + private RpcController mockController; + + private final Descriptors.MethodDescriptor fooDescriptor = + TestService.getDescriptor().getMethods().get(0); + private final Descriptors.MethodDescriptor barDescriptor = + TestService.getDescriptor().getMethods().get(1); + + protected void setUp() throws Exception { + super.setUp(); + control = EasyMock.createStrictControl(); + mockController = control.createMock(RpcController.class); + } + + // ================================================================= + + /** Tests Service.callMethod(). */ + public void testCallMethod() throws Exception { + FooRequest fooRequest = FooRequest.newBuilder().build(); + BarRequest barRequest = BarRequest.newBuilder().build(); + MockCallback fooCallback = new MockCallback(); + MockCallback barCallback = new MockCallback(); + TestService mockService = control.createMock(TestService.class); + + mockService.foo(EasyMock.same(mockController), EasyMock.same(fooRequest), + this.wrapsCallback(fooCallback)); + mockService.bar(EasyMock.same(mockController), EasyMock.same(barRequest), + this.wrapsCallback(barCallback)); + control.replay(); + + mockService.callMethod(fooDescriptor, mockController, + fooRequest, fooCallback); + mockService.callMethod(barDescriptor, mockController, + barRequest, barCallback); + control.verify(); + } + + /** Tests Service.get{Request,Response}Prototype(). */ + public void testGetPrototype() throws Exception { + TestService mockService = control.createMock(TestService.class); + + assertSame(mockService.getRequestPrototype(fooDescriptor), + FooRequest.getDefaultInstance()); + assertSame(mockService.getResponsePrototype(fooDescriptor), + FooResponse.getDefaultInstance()); + assertSame(mockService.getRequestPrototype(barDescriptor), + BarRequest.getDefaultInstance()); + assertSame(mockService.getResponsePrototype(barDescriptor), + BarResponse.getDefaultInstance()); + } + + /** Tests generated stubs. */ + public void testStub() throws Exception { + FooRequest fooRequest = FooRequest.newBuilder().build(); + BarRequest barRequest = BarRequest.newBuilder().build(); + MockCallback fooCallback = new MockCallback(); + MockCallback barCallback = new MockCallback(); + RpcChannel mockChannel = control.createMock(RpcChannel.class); + TestService stub = TestService.newStub(mockChannel); + + mockChannel.callMethod( + EasyMock.same(fooDescriptor), + EasyMock.same(mockController), + EasyMock.same(fooRequest), + EasyMock.same(FooResponse.getDefaultInstance()), + this.wrapsCallback(fooCallback)); + mockChannel.callMethod( + EasyMock.same(barDescriptor), + EasyMock.same(mockController), + EasyMock.same(barRequest), + EasyMock.same(BarResponse.getDefaultInstance()), + this.wrapsCallback(barCallback)); + control.replay(); + + stub.foo(mockController, fooRequest, fooCallback); + stub.bar(mockController, barRequest, barCallback); + control.verify(); + } + + // ================================================================= + + /** + * wrapsCallback() is an EasyMock argument predicate. wrapsCallback(c) + * matches a callback if calling that callback causes c to be called. + * In other words, c wraps the given callback. + */ + private RpcCallback wrapsCallback( + MockCallback callback) { + EasyMock.reportMatcher(new WrapsCallback(callback)); + return null; + } + + /** The parameter to wrapsCallback() must be a MockCallback. */ + private static class MockCallback + implements RpcCallback { + private boolean called = false; + + public boolean isCalled() { return called; } + + public void reset() { called = false; } + public void run(Type message) { called = true; } + } + + /** Implementation of the wrapsCallback() argument matcher. */ + private static class WrapsCallback implements IArgumentMatcher { + private MockCallback callback; + + public WrapsCallback(MockCallback callback) { + this.callback = callback; + } + + @SuppressWarnings("unchecked") + public boolean matches(Object actual) { + if (!(actual instanceof RpcCallback)) { + return false; + } + RpcCallback actualCallback = (RpcCallback)actual; + + callback.reset(); + actualCallback.run(null); + return callback.isCalled(); + } + + public void appendTo(StringBuffer buffer) { + buffer.append("wrapsCallback(mockCallback)"); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java new file mode 100644 index 00000000..af493ad1 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/TestUtil.java @@ -0,0 +1,2402 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Note: This file contains many lines over 80 characters. It even contains +// many lines over 100 characters, which fails a presubmit test. However, +// given the extremely repetitive nature of the file, I (kenton) feel that +// having similar components of each statement line up is more important than +// avoiding horizontal scrolling. So, I am bypassing the presubmit check. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.ForeignEnum; +import com.google.protobuf.test.UnittestImport.ImportMessage; +import com.google.protobuf.test.UnittestImport.ImportEnum; + +import junit.framework.Assert; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * Contains methods for setting all fields of {@code TestAllTypes} to + * some vaules as well as checking that all the fields are set to those values. + * These are useful for testing various protocol message features, e.g. + * set all fields of a message, serialize it, parse it, and check that all + * fields are set. + * + * @author kenton@google.com Kenton Varda + */ +class TestUtil { + private TestUtil() {} + + /** Helper to convert a String to ByteString. */ + private static ByteString toBytes(String str) { + try { + return ByteString.copyFrom(str.getBytes("UTF-8")); + } catch(java.io.UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported.", e); + } + } + + /** + * Get a {@code TestAllTypes} with all fields set as they would be by + * {@link #setAllFields(TestAllTypes.Builder)}. + */ + public static TestAllTypes getAllSet() { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + setAllFields(builder); + return builder.build(); + } + + /** + * Get a {@code TestAllExtensions} with all fields set as they would be by + * {@link #setAllExtensions(TestAllExtensions.Builder)}. + */ + public static TestAllExtensions getAllExtensionsSet() { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + setAllExtensions(builder); + return builder.build(); + } + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllFieldsSet()}. + */ + public static void setAllFields(TestAllTypes.Builder message) { + message.setOptionalInt32 (101); + message.setOptionalInt64 (102); + message.setOptionalUint32 (103); + message.setOptionalUint64 (104); + message.setOptionalSint32 (105); + message.setOptionalSint64 (106); + message.setOptionalFixed32 (107); + message.setOptionalFixed64 (108); + message.setOptionalSfixed32(109); + message.setOptionalSfixed64(110); + message.setOptionalFloat (111); + message.setOptionalDouble (112); + message.setOptionalBool (true); + message.setOptionalString ("115"); + message.setOptionalBytes (toBytes("116")); + + message.setOptionalGroup( + TestAllTypes.OptionalGroup.newBuilder().setA(117).build()); + message.setOptionalNestedMessage( + TestAllTypes.NestedMessage.newBuilder().setBb(118).build()); + message.setOptionalForeignMessage( + ForeignMessage.newBuilder().setC(119).build()); + message.setOptionalImportMessage( + ImportMessage.newBuilder().setD(120).build()); + + message.setOptionalNestedEnum (TestAllTypes.NestedEnum.BAZ); + message.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAZ); + message.setOptionalImportEnum (ImportEnum.IMPORT_BAZ); + + message.setOptionalStringPiece("124"); + message.setOptionalCord("125"); + + // ----------------------------------------------------------------- + + message.addRepeatedInt32 (201); + message.addRepeatedInt64 (202); + message.addRepeatedUint32 (203); + message.addRepeatedUint64 (204); + message.addRepeatedSint32 (205); + message.addRepeatedSint64 (206); + message.addRepeatedFixed32 (207); + message.addRepeatedFixed64 (208); + message.addRepeatedSfixed32(209); + message.addRepeatedSfixed64(210); + message.addRepeatedFloat (211); + message.addRepeatedDouble (212); + message.addRepeatedBool (true); + message.addRepeatedString ("215"); + message.addRepeatedBytes (toBytes("216")); + + message.addRepeatedGroup( + TestAllTypes.RepeatedGroup.newBuilder().setA(217).build()); + message.addRepeatedNestedMessage( + TestAllTypes.NestedMessage.newBuilder().setBb(218).build()); + message.addRepeatedForeignMessage( + ForeignMessage.newBuilder().setC(219).build()); + message.addRepeatedImportMessage( + ImportMessage.newBuilder().setD(220).build()); + + message.addRepeatedNestedEnum (TestAllTypes.NestedEnum.BAR); + message.addRepeatedForeignEnum(ForeignEnum.FOREIGN_BAR); + message.addRepeatedImportEnum (ImportEnum.IMPORT_BAR); + + message.addRepeatedStringPiece("224"); + message.addRepeatedCord("225"); + + // Add a second one of each field. + message.addRepeatedInt32 (301); + message.addRepeatedInt64 (302); + message.addRepeatedUint32 (303); + message.addRepeatedUint64 (304); + message.addRepeatedSint32 (305); + message.addRepeatedSint64 (306); + message.addRepeatedFixed32 (307); + message.addRepeatedFixed64 (308); + message.addRepeatedSfixed32(309); + message.addRepeatedSfixed64(310); + message.addRepeatedFloat (311); + message.addRepeatedDouble (312); + message.addRepeatedBool (false); + message.addRepeatedString ("315"); + message.addRepeatedBytes (toBytes("316")); + + message.addRepeatedGroup( + TestAllTypes.RepeatedGroup.newBuilder().setA(317).build()); + message.addRepeatedNestedMessage( + TestAllTypes.NestedMessage.newBuilder().setBb(318).build()); + message.addRepeatedForeignMessage( + ForeignMessage.newBuilder().setC(319).build()); + message.addRepeatedImportMessage( + ImportMessage.newBuilder().setD(320).build()); + + message.addRepeatedNestedEnum (TestAllTypes.NestedEnum.BAZ); + message.addRepeatedForeignEnum(ForeignEnum.FOREIGN_BAZ); + message.addRepeatedImportEnum (ImportEnum.IMPORT_BAZ); + + message.addRepeatedStringPiece("324"); + message.addRepeatedCord("325"); + + // ----------------------------------------------------------------- + + message.setDefaultInt32 (401); + message.setDefaultInt64 (402); + message.setDefaultUint32 (403); + message.setDefaultUint64 (404); + message.setDefaultSint32 (405); + message.setDefaultSint64 (406); + message.setDefaultFixed32 (407); + message.setDefaultFixed64 (408); + message.setDefaultSfixed32(409); + message.setDefaultSfixed64(410); + message.setDefaultFloat (411); + message.setDefaultDouble (412); + message.setDefaultBool (false); + message.setDefaultString ("415"); + message.setDefaultBytes (toBytes("416")); + + message.setDefaultNestedEnum (TestAllTypes.NestedEnum.FOO); + message.setDefaultForeignEnum(ForeignEnum.FOREIGN_FOO); + message.setDefaultImportEnum (ImportEnum.IMPORT_FOO); + + message.setDefaultStringPiece("424"); + message.setDefaultCord("425"); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated fields of {@code message} to contain the values + * expected by {@code assertRepeatedFieldsModified()}. + */ + public static void modifyRepeatedFields(TestAllTypes.Builder message) { + message.setRepeatedInt32 (1, 501); + message.setRepeatedInt64 (1, 502); + message.setRepeatedUint32 (1, 503); + message.setRepeatedUint64 (1, 504); + message.setRepeatedSint32 (1, 505); + message.setRepeatedSint64 (1, 506); + message.setRepeatedFixed32 (1, 507); + message.setRepeatedFixed64 (1, 508); + message.setRepeatedSfixed32(1, 509); + message.setRepeatedSfixed64(1, 510); + message.setRepeatedFloat (1, 511); + message.setRepeatedDouble (1, 512); + message.setRepeatedBool (1, true); + message.setRepeatedString (1, "515"); + message.setRepeatedBytes (1, toBytes("516")); + + message.setRepeatedGroup(1, + TestAllTypes.RepeatedGroup.newBuilder().setA(517).build()); + message.setRepeatedNestedMessage(1, + TestAllTypes.NestedMessage.newBuilder().setBb(518).build()); + message.setRepeatedForeignMessage(1, + ForeignMessage.newBuilder().setC(519).build()); + message.setRepeatedImportMessage(1, + ImportMessage.newBuilder().setD(520).build()); + + message.setRepeatedNestedEnum (1, TestAllTypes.NestedEnum.FOO); + message.setRepeatedForeignEnum(1, ForeignEnum.FOREIGN_FOO); + message.setRepeatedImportEnum (1, ImportEnum.IMPORT_FOO); + + message.setRepeatedStringPiece(1, "524"); + message.setRepeatedCord(1, "525"); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields}. + */ + public static void assertAllFieldsSet(TestAllTypes message) { + Assert.assertTrue(message.hasOptionalInt32 ()); + Assert.assertTrue(message.hasOptionalInt64 ()); + Assert.assertTrue(message.hasOptionalUint32 ()); + Assert.assertTrue(message.hasOptionalUint64 ()); + Assert.assertTrue(message.hasOptionalSint32 ()); + Assert.assertTrue(message.hasOptionalSint64 ()); + Assert.assertTrue(message.hasOptionalFixed32 ()); + Assert.assertTrue(message.hasOptionalFixed64 ()); + Assert.assertTrue(message.hasOptionalSfixed32()); + Assert.assertTrue(message.hasOptionalSfixed64()); + Assert.assertTrue(message.hasOptionalFloat ()); + Assert.assertTrue(message.hasOptionalDouble ()); + Assert.assertTrue(message.hasOptionalBool ()); + Assert.assertTrue(message.hasOptionalString ()); + Assert.assertTrue(message.hasOptionalBytes ()); + + Assert.assertTrue(message.hasOptionalGroup ()); + Assert.assertTrue(message.hasOptionalNestedMessage ()); + Assert.assertTrue(message.hasOptionalForeignMessage()); + Assert.assertTrue(message.hasOptionalImportMessage ()); + + Assert.assertTrue(message.getOptionalGroup ().hasA()); + Assert.assertTrue(message.getOptionalNestedMessage ().hasBb()); + Assert.assertTrue(message.getOptionalForeignMessage().hasC()); + Assert.assertTrue(message.getOptionalImportMessage ().hasD()); + + Assert.assertTrue(message.hasOptionalNestedEnum ()); + Assert.assertTrue(message.hasOptionalForeignEnum()); + Assert.assertTrue(message.hasOptionalImportEnum ()); + + Assert.assertTrue(message.hasOptionalStringPiece()); + Assert.assertTrue(message.hasOptionalCord()); + + Assert.assertEquals(101 , message.getOptionalInt32 ()); + Assert.assertEquals(102 , message.getOptionalInt64 ()); + Assert.assertEquals(103 , message.getOptionalUint32 ()); + Assert.assertEquals(104 , message.getOptionalUint64 ()); + Assert.assertEquals(105 , message.getOptionalSint32 ()); + Assert.assertEquals(106 , message.getOptionalSint64 ()); + Assert.assertEquals(107 , message.getOptionalFixed32 ()); + Assert.assertEquals(108 , message.getOptionalFixed64 ()); + Assert.assertEquals(109 , message.getOptionalSfixed32()); + Assert.assertEquals(110 , message.getOptionalSfixed64()); + Assert.assertEquals(111 , message.getOptionalFloat (), 0.0); + Assert.assertEquals(112 , message.getOptionalDouble (), 0.0); + Assert.assertEquals(true , message.getOptionalBool ()); + Assert.assertEquals("115", message.getOptionalString ()); + Assert.assertEquals(toBytes("116"), message.getOptionalBytes()); + + Assert.assertEquals(117, message.getOptionalGroup ().getA()); + Assert.assertEquals(118, message.getOptionalNestedMessage ().getBb()); + Assert.assertEquals(119, message.getOptionalForeignMessage().getC()); + Assert.assertEquals(120, message.getOptionalImportMessage ().getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAZ, message.getOptionalNestedEnum()); + Assert.assertEquals(ForeignEnum.FOREIGN_BAZ, message.getOptionalForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_BAZ, message.getOptionalImportEnum()); + + Assert.assertEquals("124", message.getOptionalStringPiece()); + Assert.assertEquals("125", message.getOptionalCord()); + + // ----------------------------------------------------------------- + + Assert.assertEquals(2, message.getRepeatedInt32Count ()); + Assert.assertEquals(2, message.getRepeatedInt64Count ()); + Assert.assertEquals(2, message.getRepeatedUint32Count ()); + Assert.assertEquals(2, message.getRepeatedUint64Count ()); + Assert.assertEquals(2, message.getRepeatedSint32Count ()); + Assert.assertEquals(2, message.getRepeatedSint64Count ()); + Assert.assertEquals(2, message.getRepeatedFixed32Count ()); + Assert.assertEquals(2, message.getRepeatedFixed64Count ()); + Assert.assertEquals(2, message.getRepeatedSfixed32Count()); + Assert.assertEquals(2, message.getRepeatedSfixed64Count()); + Assert.assertEquals(2, message.getRepeatedFloatCount ()); + Assert.assertEquals(2, message.getRepeatedDoubleCount ()); + Assert.assertEquals(2, message.getRepeatedBoolCount ()); + Assert.assertEquals(2, message.getRepeatedStringCount ()); + Assert.assertEquals(2, message.getRepeatedBytesCount ()); + + Assert.assertEquals(2, message.getRepeatedGroupCount ()); + Assert.assertEquals(2, message.getRepeatedNestedMessageCount ()); + Assert.assertEquals(2, message.getRepeatedForeignMessageCount()); + Assert.assertEquals(2, message.getRepeatedImportMessageCount ()); + Assert.assertEquals(2, message.getRepeatedNestedEnumCount ()); + Assert.assertEquals(2, message.getRepeatedForeignEnumCount ()); + Assert.assertEquals(2, message.getRepeatedImportEnumCount ()); + + Assert.assertEquals(2, message.getRepeatedStringPieceCount()); + Assert.assertEquals(2, message.getRepeatedCordCount()); + + Assert.assertEquals(201 , message.getRepeatedInt32 (0)); + Assert.assertEquals(202 , message.getRepeatedInt64 (0)); + Assert.assertEquals(203 , message.getRepeatedUint32 (0)); + Assert.assertEquals(204 , message.getRepeatedUint64 (0)); + Assert.assertEquals(205 , message.getRepeatedSint32 (0)); + Assert.assertEquals(206 , message.getRepeatedSint64 (0)); + Assert.assertEquals(207 , message.getRepeatedFixed32 (0)); + Assert.assertEquals(208 , message.getRepeatedFixed64 (0)); + Assert.assertEquals(209 , message.getRepeatedSfixed32(0)); + Assert.assertEquals(210 , message.getRepeatedSfixed64(0)); + Assert.assertEquals(211 , message.getRepeatedFloat (0), 0.0); + Assert.assertEquals(212 , message.getRepeatedDouble (0), 0.0); + Assert.assertEquals(true , message.getRepeatedBool (0)); + Assert.assertEquals("215", message.getRepeatedString (0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedBytes(0)); + + Assert.assertEquals(217, message.getRepeatedGroup (0).getA()); + Assert.assertEquals(218, message.getRepeatedNestedMessage (0).getBb()); + Assert.assertEquals(219, message.getRepeatedForeignMessage(0).getC()); + Assert.assertEquals(220, message.getRepeatedImportMessage (0).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAR, message.getRepeatedNestedEnum (0)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAR, message.getRepeatedForeignEnum(0)); + Assert.assertEquals(ImportEnum.IMPORT_BAR, message.getRepeatedImportEnum(0)); + + Assert.assertEquals("224", message.getRepeatedStringPiece(0)); + Assert.assertEquals("225", message.getRepeatedCord(0)); + + Assert.assertEquals(301 , message.getRepeatedInt32 (1)); + Assert.assertEquals(302 , message.getRepeatedInt64 (1)); + Assert.assertEquals(303 , message.getRepeatedUint32 (1)); + Assert.assertEquals(304 , message.getRepeatedUint64 (1)); + Assert.assertEquals(305 , message.getRepeatedSint32 (1)); + Assert.assertEquals(306 , message.getRepeatedSint64 (1)); + Assert.assertEquals(307 , message.getRepeatedFixed32 (1)); + Assert.assertEquals(308 , message.getRepeatedFixed64 (1)); + Assert.assertEquals(309 , message.getRepeatedSfixed32(1)); + Assert.assertEquals(310 , message.getRepeatedSfixed64(1)); + Assert.assertEquals(311 , message.getRepeatedFloat (1), 0.0); + Assert.assertEquals(312 , message.getRepeatedDouble (1), 0.0); + Assert.assertEquals(false, message.getRepeatedBool (1)); + Assert.assertEquals("315", message.getRepeatedString (1)); + Assert.assertEquals(toBytes("316"), message.getRepeatedBytes(1)); + + Assert.assertEquals(317, message.getRepeatedGroup (1).getA()); + Assert.assertEquals(318, message.getRepeatedNestedMessage (1).getBb()); + Assert.assertEquals(319, message.getRepeatedForeignMessage(1).getC()); + Assert.assertEquals(320, message.getRepeatedImportMessage (1).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAZ, message.getRepeatedNestedEnum (1)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAZ, message.getRepeatedForeignEnum(1)); + Assert.assertEquals(ImportEnum.IMPORT_BAZ, message.getRepeatedImportEnum(1)); + + Assert.assertEquals("324", message.getRepeatedStringPiece(1)); + Assert.assertEquals("325", message.getRepeatedCord(1)); + + // ----------------------------------------------------------------- + + Assert.assertTrue(message.hasDefaultInt32 ()); + Assert.assertTrue(message.hasDefaultInt64 ()); + Assert.assertTrue(message.hasDefaultUint32 ()); + Assert.assertTrue(message.hasDefaultUint64 ()); + Assert.assertTrue(message.hasDefaultSint32 ()); + Assert.assertTrue(message.hasDefaultSint64 ()); + Assert.assertTrue(message.hasDefaultFixed32 ()); + Assert.assertTrue(message.hasDefaultFixed64 ()); + Assert.assertTrue(message.hasDefaultSfixed32()); + Assert.assertTrue(message.hasDefaultSfixed64()); + Assert.assertTrue(message.hasDefaultFloat ()); + Assert.assertTrue(message.hasDefaultDouble ()); + Assert.assertTrue(message.hasDefaultBool ()); + Assert.assertTrue(message.hasDefaultString ()); + Assert.assertTrue(message.hasDefaultBytes ()); + + Assert.assertTrue(message.hasDefaultNestedEnum ()); + Assert.assertTrue(message.hasDefaultForeignEnum()); + Assert.assertTrue(message.hasDefaultImportEnum ()); + + Assert.assertTrue(message.hasDefaultStringPiece()); + Assert.assertTrue(message.hasDefaultCord()); + + Assert.assertEquals(401 , message.getDefaultInt32 ()); + Assert.assertEquals(402 , message.getDefaultInt64 ()); + Assert.assertEquals(403 , message.getDefaultUint32 ()); + Assert.assertEquals(404 , message.getDefaultUint64 ()); + Assert.assertEquals(405 , message.getDefaultSint32 ()); + Assert.assertEquals(406 , message.getDefaultSint64 ()); + Assert.assertEquals(407 , message.getDefaultFixed32 ()); + Assert.assertEquals(408 , message.getDefaultFixed64 ()); + Assert.assertEquals(409 , message.getDefaultSfixed32()); + Assert.assertEquals(410 , message.getDefaultSfixed64()); + Assert.assertEquals(411 , message.getDefaultFloat (), 0.0); + Assert.assertEquals(412 , message.getDefaultDouble (), 0.0); + Assert.assertEquals(false, message.getDefaultBool ()); + Assert.assertEquals("415", message.getDefaultString ()); + Assert.assertEquals(toBytes("416"), message.getDefaultBytes()); + + Assert.assertEquals(TestAllTypes.NestedEnum.FOO, message.getDefaultNestedEnum ()); + Assert.assertEquals(ForeignEnum.FOREIGN_FOO, message.getDefaultForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_FOO, message.getDefaultImportEnum()); + + Assert.assertEquals("424", message.getDefaultStringPiece()); + Assert.assertEquals("425", message.getDefaultCord()); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are cleared, and that getting the fields returns their + * default values. + */ + public static void assertClear(TestAllTypes message) { + // hasBlah() should initially be false for all optional fields. + Assert.assertFalse(message.hasOptionalInt32 ()); + Assert.assertFalse(message.hasOptionalInt64 ()); + Assert.assertFalse(message.hasOptionalUint32 ()); + Assert.assertFalse(message.hasOptionalUint64 ()); + Assert.assertFalse(message.hasOptionalSint32 ()); + Assert.assertFalse(message.hasOptionalSint64 ()); + Assert.assertFalse(message.hasOptionalFixed32 ()); + Assert.assertFalse(message.hasOptionalFixed64 ()); + Assert.assertFalse(message.hasOptionalSfixed32()); + Assert.assertFalse(message.hasOptionalSfixed64()); + Assert.assertFalse(message.hasOptionalFloat ()); + Assert.assertFalse(message.hasOptionalDouble ()); + Assert.assertFalse(message.hasOptionalBool ()); + Assert.assertFalse(message.hasOptionalString ()); + Assert.assertFalse(message.hasOptionalBytes ()); + + Assert.assertFalse(message.hasOptionalGroup ()); + Assert.assertFalse(message.hasOptionalNestedMessage ()); + Assert.assertFalse(message.hasOptionalForeignMessage()); + Assert.assertFalse(message.hasOptionalImportMessage ()); + + Assert.assertFalse(message.hasOptionalNestedEnum ()); + Assert.assertFalse(message.hasOptionalForeignEnum()); + Assert.assertFalse(message.hasOptionalImportEnum ()); + + Assert.assertFalse(message.hasOptionalStringPiece()); + Assert.assertFalse(message.hasOptionalCord()); + + // Optional fields without defaults are set to zero or something like it. + Assert.assertEquals(0 , message.getOptionalInt32 ()); + Assert.assertEquals(0 , message.getOptionalInt64 ()); + Assert.assertEquals(0 , message.getOptionalUint32 ()); + Assert.assertEquals(0 , message.getOptionalUint64 ()); + Assert.assertEquals(0 , message.getOptionalSint32 ()); + Assert.assertEquals(0 , message.getOptionalSint64 ()); + Assert.assertEquals(0 , message.getOptionalFixed32 ()); + Assert.assertEquals(0 , message.getOptionalFixed64 ()); + Assert.assertEquals(0 , message.getOptionalSfixed32()); + Assert.assertEquals(0 , message.getOptionalSfixed64()); + Assert.assertEquals(0 , message.getOptionalFloat (), 0.0); + Assert.assertEquals(0 , message.getOptionalDouble (), 0.0); + Assert.assertEquals(false, message.getOptionalBool ()); + Assert.assertEquals("" , message.getOptionalString ()); + Assert.assertEquals(ByteString.EMPTY, message.getOptionalBytes()); + + // Embedded messages should also be clear. + Assert.assertFalse(message.getOptionalGroup ().hasA()); + Assert.assertFalse(message.getOptionalNestedMessage ().hasBb()); + Assert.assertFalse(message.getOptionalForeignMessage().hasC()); + Assert.assertFalse(message.getOptionalImportMessage ().hasD()); + + Assert.assertEquals(0, message.getOptionalGroup ().getA()); + Assert.assertEquals(0, message.getOptionalNestedMessage ().getBb()); + Assert.assertEquals(0, message.getOptionalForeignMessage().getC()); + Assert.assertEquals(0, message.getOptionalImportMessage ().getD()); + + // Enums without defaults are set to the first value in the enum. + Assert.assertEquals(TestAllTypes.NestedEnum.FOO, message.getOptionalNestedEnum ()); + Assert.assertEquals(ForeignEnum.FOREIGN_FOO, message.getOptionalForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_FOO, message.getOptionalImportEnum()); + + Assert.assertEquals("", message.getOptionalStringPiece()); + Assert.assertEquals("", message.getOptionalCord()); + + // Repeated fields are empty. + Assert.assertEquals(0, message.getRepeatedInt32Count ()); + Assert.assertEquals(0, message.getRepeatedInt64Count ()); + Assert.assertEquals(0, message.getRepeatedUint32Count ()); + Assert.assertEquals(0, message.getRepeatedUint64Count ()); + Assert.assertEquals(0, message.getRepeatedSint32Count ()); + Assert.assertEquals(0, message.getRepeatedSint64Count ()); + Assert.assertEquals(0, message.getRepeatedFixed32Count ()); + Assert.assertEquals(0, message.getRepeatedFixed64Count ()); + Assert.assertEquals(0, message.getRepeatedSfixed32Count()); + Assert.assertEquals(0, message.getRepeatedSfixed64Count()); + Assert.assertEquals(0, message.getRepeatedFloatCount ()); + Assert.assertEquals(0, message.getRepeatedDoubleCount ()); + Assert.assertEquals(0, message.getRepeatedBoolCount ()); + Assert.assertEquals(0, message.getRepeatedStringCount ()); + Assert.assertEquals(0, message.getRepeatedBytesCount ()); + + Assert.assertEquals(0, message.getRepeatedGroupCount ()); + Assert.assertEquals(0, message.getRepeatedNestedMessageCount ()); + Assert.assertEquals(0, message.getRepeatedForeignMessageCount()); + Assert.assertEquals(0, message.getRepeatedImportMessageCount ()); + Assert.assertEquals(0, message.getRepeatedNestedEnumCount ()); + Assert.assertEquals(0, message.getRepeatedForeignEnumCount ()); + Assert.assertEquals(0, message.getRepeatedImportEnumCount ()); + + Assert.assertEquals(0, message.getRepeatedStringPieceCount()); + Assert.assertEquals(0, message.getRepeatedCordCount()); + + // hasBlah() should also be false for all default fields. + Assert.assertFalse(message.hasDefaultInt32 ()); + Assert.assertFalse(message.hasDefaultInt64 ()); + Assert.assertFalse(message.hasDefaultUint32 ()); + Assert.assertFalse(message.hasDefaultUint64 ()); + Assert.assertFalse(message.hasDefaultSint32 ()); + Assert.assertFalse(message.hasDefaultSint64 ()); + Assert.assertFalse(message.hasDefaultFixed32 ()); + Assert.assertFalse(message.hasDefaultFixed64 ()); + Assert.assertFalse(message.hasDefaultSfixed32()); + Assert.assertFalse(message.hasDefaultSfixed64()); + Assert.assertFalse(message.hasDefaultFloat ()); + Assert.assertFalse(message.hasDefaultDouble ()); + Assert.assertFalse(message.hasDefaultBool ()); + Assert.assertFalse(message.hasDefaultString ()); + Assert.assertFalse(message.hasDefaultBytes ()); + + Assert.assertFalse(message.hasDefaultNestedEnum ()); + Assert.assertFalse(message.hasDefaultForeignEnum()); + Assert.assertFalse(message.hasDefaultImportEnum ()); + + Assert.assertFalse(message.hasDefaultStringPiece()); + Assert.assertFalse(message.hasDefaultCord()); + + // Fields with defaults have their default values (duh). + Assert.assertEquals( 41 , message.getDefaultInt32 ()); + Assert.assertEquals( 42 , message.getDefaultInt64 ()); + Assert.assertEquals( 43 , message.getDefaultUint32 ()); + Assert.assertEquals( 44 , message.getDefaultUint64 ()); + Assert.assertEquals(-45 , message.getDefaultSint32 ()); + Assert.assertEquals( 46 , message.getDefaultSint64 ()); + Assert.assertEquals( 47 , message.getDefaultFixed32 ()); + Assert.assertEquals( 48 , message.getDefaultFixed64 ()); + Assert.assertEquals( 49 , message.getDefaultSfixed32()); + Assert.assertEquals(-50 , message.getDefaultSfixed64()); + Assert.assertEquals( 51.5 , message.getDefaultFloat (), 0.0); + Assert.assertEquals( 52e3 , message.getDefaultDouble (), 0.0); + Assert.assertEquals(true , message.getDefaultBool ()); + Assert.assertEquals("hello", message.getDefaultString ()); + Assert.assertEquals(toBytes("world"), message.getDefaultBytes()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAR, message.getDefaultNestedEnum ()); + Assert.assertEquals(ForeignEnum.FOREIGN_BAR, message.getDefaultForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_BAR, message.getDefaultImportEnum()); + + Assert.assertEquals("abc", message.getDefaultStringPiece()); + Assert.assertEquals("123", message.getDefaultCord()); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields} + * followed by {@code modifyRepeatedFields}. + */ + public static void assertRepeatedFieldsModified(TestAllTypes message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + Assert.assertEquals(2, message.getRepeatedInt32Count ()); + Assert.assertEquals(2, message.getRepeatedInt64Count ()); + Assert.assertEquals(2, message.getRepeatedUint32Count ()); + Assert.assertEquals(2, message.getRepeatedUint64Count ()); + Assert.assertEquals(2, message.getRepeatedSint32Count ()); + Assert.assertEquals(2, message.getRepeatedSint64Count ()); + Assert.assertEquals(2, message.getRepeatedFixed32Count ()); + Assert.assertEquals(2, message.getRepeatedFixed64Count ()); + Assert.assertEquals(2, message.getRepeatedSfixed32Count()); + Assert.assertEquals(2, message.getRepeatedSfixed64Count()); + Assert.assertEquals(2, message.getRepeatedFloatCount ()); + Assert.assertEquals(2, message.getRepeatedDoubleCount ()); + Assert.assertEquals(2, message.getRepeatedBoolCount ()); + Assert.assertEquals(2, message.getRepeatedStringCount ()); + Assert.assertEquals(2, message.getRepeatedBytesCount ()); + + Assert.assertEquals(2, message.getRepeatedGroupCount ()); + Assert.assertEquals(2, message.getRepeatedNestedMessageCount ()); + Assert.assertEquals(2, message.getRepeatedForeignMessageCount()); + Assert.assertEquals(2, message.getRepeatedImportMessageCount ()); + Assert.assertEquals(2, message.getRepeatedNestedEnumCount ()); + Assert.assertEquals(2, message.getRepeatedForeignEnumCount ()); + Assert.assertEquals(2, message.getRepeatedImportEnumCount ()); + + Assert.assertEquals(2, message.getRepeatedStringPieceCount()); + Assert.assertEquals(2, message.getRepeatedCordCount()); + + Assert.assertEquals(201 , message.getRepeatedInt32 (0)); + Assert.assertEquals(202L , message.getRepeatedInt64 (0)); + Assert.assertEquals(203 , message.getRepeatedUint32 (0)); + Assert.assertEquals(204L , message.getRepeatedUint64 (0)); + Assert.assertEquals(205 , message.getRepeatedSint32 (0)); + Assert.assertEquals(206L , message.getRepeatedSint64 (0)); + Assert.assertEquals(207 , message.getRepeatedFixed32 (0)); + Assert.assertEquals(208L , message.getRepeatedFixed64 (0)); + Assert.assertEquals(209 , message.getRepeatedSfixed32(0)); + Assert.assertEquals(210L , message.getRepeatedSfixed64(0)); + Assert.assertEquals(211F , message.getRepeatedFloat (0)); + Assert.assertEquals(212D , message.getRepeatedDouble (0)); + Assert.assertEquals(true , message.getRepeatedBool (0)); + Assert.assertEquals("215", message.getRepeatedString (0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedBytes(0)); + + Assert.assertEquals(217, message.getRepeatedGroup (0).getA()); + Assert.assertEquals(218, message.getRepeatedNestedMessage (0).getBb()); + Assert.assertEquals(219, message.getRepeatedForeignMessage(0).getC()); + Assert.assertEquals(220, message.getRepeatedImportMessage (0).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAR, message.getRepeatedNestedEnum (0)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAR, message.getRepeatedForeignEnum(0)); + Assert.assertEquals(ImportEnum.IMPORT_BAR, message.getRepeatedImportEnum(0)); + + Assert.assertEquals("224", message.getRepeatedStringPiece(0)); + Assert.assertEquals("225", message.getRepeatedCord(0)); + + // Actually verify the second (modified) elements now. + Assert.assertEquals(501 , message.getRepeatedInt32 (1)); + Assert.assertEquals(502L , message.getRepeatedInt64 (1)); + Assert.assertEquals(503 , message.getRepeatedUint32 (1)); + Assert.assertEquals(504L , message.getRepeatedUint64 (1)); + Assert.assertEquals(505 , message.getRepeatedSint32 (1)); + Assert.assertEquals(506L , message.getRepeatedSint64 (1)); + Assert.assertEquals(507 , message.getRepeatedFixed32 (1)); + Assert.assertEquals(508L , message.getRepeatedFixed64 (1)); + Assert.assertEquals(509 , message.getRepeatedSfixed32(1)); + Assert.assertEquals(510L , message.getRepeatedSfixed64(1)); + Assert.assertEquals(511F , message.getRepeatedFloat (1)); + Assert.assertEquals(512D , message.getRepeatedDouble (1)); + Assert.assertEquals(true , message.getRepeatedBool (1)); + Assert.assertEquals("515", message.getRepeatedString (1)); + Assert.assertEquals(toBytes("516"), message.getRepeatedBytes(1)); + + Assert.assertEquals(517, message.getRepeatedGroup (1).getA()); + Assert.assertEquals(518, message.getRepeatedNestedMessage (1).getBb()); + Assert.assertEquals(519, message.getRepeatedForeignMessage(1).getC()); + Assert.assertEquals(520, message.getRepeatedImportMessage (1).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.FOO, message.getRepeatedNestedEnum (1)); + Assert.assertEquals(ForeignEnum.FOREIGN_FOO, message.getRepeatedForeignEnum(1)); + Assert.assertEquals(ImportEnum.IMPORT_FOO, message.getRepeatedImportEnum(1)); + + Assert.assertEquals("524", message.getRepeatedStringPiece(1)); + Assert.assertEquals("525", message.getRepeatedCord(1)); + } + + // =================================================================== + // Like above, but for extensions + + // Java gets confused with things like assertEquals(int, Integer): it can't + // decide whether to call assertEquals(int, int) or assertEquals(Object, + // Object). So we define these methods to help it. + private static void assertEqualsExactType(int a, int b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(long a, long b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(float a, float b) { + Assert.assertEquals(a, b, 0.0); + } + private static void assertEqualsExactType(double a, double b) { + Assert.assertEquals(a, b, 0.0); + } + private static void assertEqualsExactType(boolean a, boolean b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(String a, String b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(ByteString a, ByteString b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(TestAllTypes.NestedEnum a, + TestAllTypes.NestedEnum b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(ForeignEnum a, ForeignEnum b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(ImportEnum a, ImportEnum b) { + Assert.assertEquals(a, b); + } + + /** + * Get an unmodifiable {@link ExtensionRegistry} containing all the + * extensions of {@code TestAllExtensions}. + */ + public static ExtensionRegistry getExtensionRegistry() { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + registerAllExtensions(registry); + return registry.getUnmodifiable(); + } + + /** + * Register all of {@code TestAllExtensions}' extensions with the + * given {@link ExtensionRegistry}. + */ + public static void registerAllExtensions(ExtensionRegistry registry) { + registry.add(UnittestProto.optionalInt32Extension ); + registry.add(UnittestProto.optionalInt64Extension ); + registry.add(UnittestProto.optionalUint32Extension ); + registry.add(UnittestProto.optionalUint64Extension ); + registry.add(UnittestProto.optionalSint32Extension ); + registry.add(UnittestProto.optionalSint64Extension ); + registry.add(UnittestProto.optionalFixed32Extension ); + registry.add(UnittestProto.optionalFixed64Extension ); + registry.add(UnittestProto.optionalSfixed32Extension ); + registry.add(UnittestProto.optionalSfixed64Extension ); + registry.add(UnittestProto.optionalFloatExtension ); + registry.add(UnittestProto.optionalDoubleExtension ); + registry.add(UnittestProto.optionalBoolExtension ); + registry.add(UnittestProto.optionalStringExtension ); + registry.add(UnittestProto.optionalBytesExtension ); + registry.add(UnittestProto.optionalGroupExtension ); + registry.add(UnittestProto.optionalNestedMessageExtension ); + registry.add(UnittestProto.optionalForeignMessageExtension); + registry.add(UnittestProto.optionalImportMessageExtension ); + registry.add(UnittestProto.optionalNestedEnumExtension ); + registry.add(UnittestProto.optionalForeignEnumExtension ); + registry.add(UnittestProto.optionalImportEnumExtension ); + registry.add(UnittestProto.optionalStringPieceExtension ); + registry.add(UnittestProto.optionalCordExtension ); + + registry.add(UnittestProto.repeatedInt32Extension ); + registry.add(UnittestProto.repeatedInt64Extension ); + registry.add(UnittestProto.repeatedUint32Extension ); + registry.add(UnittestProto.repeatedUint64Extension ); + registry.add(UnittestProto.repeatedSint32Extension ); + registry.add(UnittestProto.repeatedSint64Extension ); + registry.add(UnittestProto.repeatedFixed32Extension ); + registry.add(UnittestProto.repeatedFixed64Extension ); + registry.add(UnittestProto.repeatedSfixed32Extension ); + registry.add(UnittestProto.repeatedSfixed64Extension ); + registry.add(UnittestProto.repeatedFloatExtension ); + registry.add(UnittestProto.repeatedDoubleExtension ); + registry.add(UnittestProto.repeatedBoolExtension ); + registry.add(UnittestProto.repeatedStringExtension ); + registry.add(UnittestProto.repeatedBytesExtension ); + registry.add(UnittestProto.repeatedGroupExtension ); + registry.add(UnittestProto.repeatedNestedMessageExtension ); + registry.add(UnittestProto.repeatedForeignMessageExtension); + registry.add(UnittestProto.repeatedImportMessageExtension ); + registry.add(UnittestProto.repeatedNestedEnumExtension ); + registry.add(UnittestProto.repeatedForeignEnumExtension ); + registry.add(UnittestProto.repeatedImportEnumExtension ); + registry.add(UnittestProto.repeatedStringPieceExtension ); + registry.add(UnittestProto.repeatedCordExtension ); + + registry.add(UnittestProto.defaultInt32Extension ); + registry.add(UnittestProto.defaultInt64Extension ); + registry.add(UnittestProto.defaultUint32Extension ); + registry.add(UnittestProto.defaultUint64Extension ); + registry.add(UnittestProto.defaultSint32Extension ); + registry.add(UnittestProto.defaultSint64Extension ); + registry.add(UnittestProto.defaultFixed32Extension ); + registry.add(UnittestProto.defaultFixed64Extension ); + registry.add(UnittestProto.defaultSfixed32Extension ); + registry.add(UnittestProto.defaultSfixed64Extension ); + registry.add(UnittestProto.defaultFloatExtension ); + registry.add(UnittestProto.defaultDoubleExtension ); + registry.add(UnittestProto.defaultBoolExtension ); + registry.add(UnittestProto.defaultStringExtension ); + registry.add(UnittestProto.defaultBytesExtension ); + registry.add(UnittestProto.defaultNestedEnumExtension ); + registry.add(UnittestProto.defaultForeignEnumExtension); + registry.add(UnittestProto.defaultImportEnumExtension ); + registry.add(UnittestProto.defaultStringPieceExtension); + registry.add(UnittestProto.defaultCordExtension ); + } + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllExtensionsSet()}. + */ + public static void setAllExtensions(TestAllExtensions.Builder message) { + message.setExtension(UnittestProto.optionalInt32Extension , 101); + message.setExtension(UnittestProto.optionalInt64Extension , 102L); + message.setExtension(UnittestProto.optionalUint32Extension , 103); + message.setExtension(UnittestProto.optionalUint64Extension , 104L); + message.setExtension(UnittestProto.optionalSint32Extension , 105); + message.setExtension(UnittestProto.optionalSint64Extension , 106L); + message.setExtension(UnittestProto.optionalFixed32Extension , 107); + message.setExtension(UnittestProto.optionalFixed64Extension , 108L); + message.setExtension(UnittestProto.optionalSfixed32Extension, 109); + message.setExtension(UnittestProto.optionalSfixed64Extension, 110L); + message.setExtension(UnittestProto.optionalFloatExtension , 111F); + message.setExtension(UnittestProto.optionalDoubleExtension , 112D); + message.setExtension(UnittestProto.optionalBoolExtension , true); + message.setExtension(UnittestProto.optionalStringExtension , "115"); + message.setExtension(UnittestProto.optionalBytesExtension , toBytes("116")); + + message.setExtension(UnittestProto.optionalGroupExtension, + UnittestProto.OptionalGroup_extension.newBuilder().setA(117).build()); + message.setExtension(UnittestProto.optionalNestedMessageExtension, + TestAllTypes.NestedMessage.newBuilder().setBb(118).build()); + message.setExtension(UnittestProto.optionalForeignMessageExtension, + ForeignMessage.newBuilder().setC(119).build()); + message.setExtension(UnittestProto.optionalImportMessageExtension, + ImportMessage.newBuilder().setD(120).build()); + + message.setExtension(UnittestProto.optionalNestedEnumExtension, + TestAllTypes.NestedEnum.BAZ); + message.setExtension(UnittestProto.optionalForeignEnumExtension, + ForeignEnum.FOREIGN_BAZ); + message.setExtension(UnittestProto.optionalImportEnumExtension, + ImportEnum.IMPORT_BAZ); + + message.setExtension(UnittestProto.optionalStringPieceExtension, "124"); + message.setExtension(UnittestProto.optionalCordExtension, "125"); + + // ----------------------------------------------------------------- + + message.addExtension(UnittestProto.repeatedInt32Extension , 201); + message.addExtension(UnittestProto.repeatedInt64Extension , 202L); + message.addExtension(UnittestProto.repeatedUint32Extension , 203); + message.addExtension(UnittestProto.repeatedUint64Extension , 204L); + message.addExtension(UnittestProto.repeatedSint32Extension , 205); + message.addExtension(UnittestProto.repeatedSint64Extension , 206L); + message.addExtension(UnittestProto.repeatedFixed32Extension , 207); + message.addExtension(UnittestProto.repeatedFixed64Extension , 208L); + message.addExtension(UnittestProto.repeatedSfixed32Extension, 209); + message.addExtension(UnittestProto.repeatedSfixed64Extension, 210L); + message.addExtension(UnittestProto.repeatedFloatExtension , 211F); + message.addExtension(UnittestProto.repeatedDoubleExtension , 212D); + message.addExtension(UnittestProto.repeatedBoolExtension , true); + message.addExtension(UnittestProto.repeatedStringExtension , "215"); + message.addExtension(UnittestProto.repeatedBytesExtension , toBytes("216")); + + message.addExtension(UnittestProto.repeatedGroupExtension, + UnittestProto.RepeatedGroup_extension.newBuilder().setA(217).build()); + message.addExtension(UnittestProto.repeatedNestedMessageExtension, + TestAllTypes.NestedMessage.newBuilder().setBb(218).build()); + message.addExtension(UnittestProto.repeatedForeignMessageExtension, + ForeignMessage.newBuilder().setC(219).build()); + message.addExtension(UnittestProto.repeatedImportMessageExtension, + ImportMessage.newBuilder().setD(220).build()); + + message.addExtension(UnittestProto.repeatedNestedEnumExtension, + TestAllTypes.NestedEnum.BAR); + message.addExtension(UnittestProto.repeatedForeignEnumExtension, + ForeignEnum.FOREIGN_BAR); + message.addExtension(UnittestProto.repeatedImportEnumExtension, + ImportEnum.IMPORT_BAR); + + message.addExtension(UnittestProto.repeatedStringPieceExtension, "224"); + message.addExtension(UnittestProto.repeatedCordExtension, "225"); + + // Add a second one of each field. + message.addExtension(UnittestProto.repeatedInt32Extension , 301); + message.addExtension(UnittestProto.repeatedInt64Extension , 302L); + message.addExtension(UnittestProto.repeatedUint32Extension , 303); + message.addExtension(UnittestProto.repeatedUint64Extension , 304L); + message.addExtension(UnittestProto.repeatedSint32Extension , 305); + message.addExtension(UnittestProto.repeatedSint64Extension , 306L); + message.addExtension(UnittestProto.repeatedFixed32Extension , 307); + message.addExtension(UnittestProto.repeatedFixed64Extension , 308L); + message.addExtension(UnittestProto.repeatedSfixed32Extension, 309); + message.addExtension(UnittestProto.repeatedSfixed64Extension, 310L); + message.addExtension(UnittestProto.repeatedFloatExtension , 311F); + message.addExtension(UnittestProto.repeatedDoubleExtension , 312D); + message.addExtension(UnittestProto.repeatedBoolExtension , false); + message.addExtension(UnittestProto.repeatedStringExtension , "315"); + message.addExtension(UnittestProto.repeatedBytesExtension , toBytes("316")); + + message.addExtension(UnittestProto.repeatedGroupExtension, + UnittestProto.RepeatedGroup_extension.newBuilder().setA(317).build()); + message.addExtension(UnittestProto.repeatedNestedMessageExtension, + TestAllTypes.NestedMessage.newBuilder().setBb(318).build()); + message.addExtension(UnittestProto.repeatedForeignMessageExtension, + ForeignMessage.newBuilder().setC(319).build()); + message.addExtension(UnittestProto.repeatedImportMessageExtension, + ImportMessage.newBuilder().setD(320).build()); + + message.addExtension(UnittestProto.repeatedNestedEnumExtension, + TestAllTypes.NestedEnum.BAZ); + message.addExtension(UnittestProto.repeatedForeignEnumExtension, + ForeignEnum.FOREIGN_BAZ); + message.addExtension(UnittestProto.repeatedImportEnumExtension, + ImportEnum.IMPORT_BAZ); + + message.addExtension(UnittestProto.repeatedStringPieceExtension, "324"); + message.addExtension(UnittestProto.repeatedCordExtension, "325"); + + // ----------------------------------------------------------------- + + message.setExtension(UnittestProto.defaultInt32Extension , 401); + message.setExtension(UnittestProto.defaultInt64Extension , 402L); + message.setExtension(UnittestProto.defaultUint32Extension , 403); + message.setExtension(UnittestProto.defaultUint64Extension , 404L); + message.setExtension(UnittestProto.defaultSint32Extension , 405); + message.setExtension(UnittestProto.defaultSint64Extension , 406L); + message.setExtension(UnittestProto.defaultFixed32Extension , 407); + message.setExtension(UnittestProto.defaultFixed64Extension , 408L); + message.setExtension(UnittestProto.defaultSfixed32Extension, 409); + message.setExtension(UnittestProto.defaultSfixed64Extension, 410L); + message.setExtension(UnittestProto.defaultFloatExtension , 411F); + message.setExtension(UnittestProto.defaultDoubleExtension , 412D); + message.setExtension(UnittestProto.defaultBoolExtension , false); + message.setExtension(UnittestProto.defaultStringExtension , "415"); + message.setExtension(UnittestProto.defaultBytesExtension , toBytes("416")); + + message.setExtension(UnittestProto.defaultNestedEnumExtension, + TestAllTypes.NestedEnum.FOO); + message.setExtension(UnittestProto.defaultForeignEnumExtension, + ForeignEnum.FOREIGN_FOO); + message.setExtension(UnittestProto.defaultImportEnumExtension, + ImportEnum.IMPORT_FOO); + + message.setExtension(UnittestProto.defaultStringPieceExtension, "424"); + message.setExtension(UnittestProto.defaultCordExtension, "425"); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated extensions of {@code message} to contain the values + * expected by {@code assertRepeatedExtensionsModified()}. + */ + public static void modifyRepeatedExtensions( + TestAllExtensions.Builder message) { + message.setExtension(UnittestProto.repeatedInt32Extension , 1, 501); + message.setExtension(UnittestProto.repeatedInt64Extension , 1, 502L); + message.setExtension(UnittestProto.repeatedUint32Extension , 1, 503); + message.setExtension(UnittestProto.repeatedUint64Extension , 1, 504L); + message.setExtension(UnittestProto.repeatedSint32Extension , 1, 505); + message.setExtension(UnittestProto.repeatedSint64Extension , 1, 506L); + message.setExtension(UnittestProto.repeatedFixed32Extension , 1, 507); + message.setExtension(UnittestProto.repeatedFixed64Extension , 1, 508L); + message.setExtension(UnittestProto.repeatedSfixed32Extension, 1, 509); + message.setExtension(UnittestProto.repeatedSfixed64Extension, 1, 510L); + message.setExtension(UnittestProto.repeatedFloatExtension , 1, 511F); + message.setExtension(UnittestProto.repeatedDoubleExtension , 1, 512D); + message.setExtension(UnittestProto.repeatedBoolExtension , 1, true); + message.setExtension(UnittestProto.repeatedStringExtension , 1, "515"); + message.setExtension(UnittestProto.repeatedBytesExtension , 1, toBytes("516")); + + message.setExtension(UnittestProto.repeatedGroupExtension, 1, + UnittestProto.RepeatedGroup_extension.newBuilder().setA(517).build()); + message.setExtension(UnittestProto.repeatedNestedMessageExtension, 1, + TestAllTypes.NestedMessage.newBuilder().setBb(518).build()); + message.setExtension(UnittestProto.repeatedForeignMessageExtension, 1, + ForeignMessage.newBuilder().setC(519).build()); + message.setExtension(UnittestProto.repeatedImportMessageExtension, 1, + ImportMessage.newBuilder().setD(520).build()); + + message.setExtension(UnittestProto.repeatedNestedEnumExtension , 1, + TestAllTypes.NestedEnum.FOO); + message.setExtension(UnittestProto.repeatedForeignEnumExtension, 1, + ForeignEnum.FOREIGN_FOO); + message.setExtension(UnittestProto.repeatedImportEnumExtension , 1, + ImportEnum.IMPORT_FOO); + + message.setExtension(UnittestProto.repeatedStringPieceExtension, 1, "524"); + message.setExtension(UnittestProto.repeatedCordExtension, 1, "525"); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all extensions of + * {@code message} are set to the values assigned by {@code setAllExtensions}. + */ + public static void assertAllExtensionsSet(TestAllExtensions message) { + Assert.assertTrue(message.hasExtension(UnittestProto.optionalInt32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalInt64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalUint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalUint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalFixed32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalFixed64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSfixed32Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSfixed64Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalFloatExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalDoubleExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalBoolExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalStringExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalBytesExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.optionalGroupExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalNestedMessageExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalForeignMessageExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalImportMessageExtension )); + + Assert.assertTrue(message.getExtension(UnittestProto.optionalGroupExtension ).hasA()); + Assert.assertTrue(message.getExtension(UnittestProto.optionalNestedMessageExtension ).hasBb()); + Assert.assertTrue(message.getExtension(UnittestProto.optionalForeignMessageExtension).hasC()); + Assert.assertTrue(message.getExtension(UnittestProto.optionalImportMessageExtension ).hasD()); + + Assert.assertTrue(message.hasExtension(UnittestProto.optionalNestedEnumExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalForeignEnumExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalImportEnumExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.optionalStringPieceExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalCordExtension)); + + assertEqualsExactType(101 , message.getExtension(UnittestProto.optionalInt32Extension )); + assertEqualsExactType(102L , message.getExtension(UnittestProto.optionalInt64Extension )); + assertEqualsExactType(103 , message.getExtension(UnittestProto.optionalUint32Extension )); + assertEqualsExactType(104L , message.getExtension(UnittestProto.optionalUint64Extension )); + assertEqualsExactType(105 , message.getExtension(UnittestProto.optionalSint32Extension )); + assertEqualsExactType(106L , message.getExtension(UnittestProto.optionalSint64Extension )); + assertEqualsExactType(107 , message.getExtension(UnittestProto.optionalFixed32Extension )); + assertEqualsExactType(108L , message.getExtension(UnittestProto.optionalFixed64Extension )); + assertEqualsExactType(109 , message.getExtension(UnittestProto.optionalSfixed32Extension)); + assertEqualsExactType(110L , message.getExtension(UnittestProto.optionalSfixed64Extension)); + assertEqualsExactType(111F , message.getExtension(UnittestProto.optionalFloatExtension )); + assertEqualsExactType(112D , message.getExtension(UnittestProto.optionalDoubleExtension )); + assertEqualsExactType(true , message.getExtension(UnittestProto.optionalBoolExtension )); + assertEqualsExactType("115", message.getExtension(UnittestProto.optionalStringExtension )); + assertEqualsExactType(toBytes("116"), message.getExtension(UnittestProto.optionalBytesExtension)); + + assertEqualsExactType(117, message.getExtension(UnittestProto.optionalGroupExtension ).getA()); + assertEqualsExactType(118, message.getExtension(UnittestProto.optionalNestedMessageExtension ).getBb()); + assertEqualsExactType(119, message.getExtension(UnittestProto.optionalForeignMessageExtension).getC()); + assertEqualsExactType(120, message.getExtension(UnittestProto.optionalImportMessageExtension ).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAZ, + message.getExtension(UnittestProto.optionalNestedEnumExtension)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAZ, + message.getExtension(UnittestProto.optionalForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_BAZ, + message.getExtension(UnittestProto.optionalImportEnumExtension)); + + assertEqualsExactType("124", message.getExtension(UnittestProto.optionalStringPieceExtension)); + assertEqualsExactType("125", message.getExtension(UnittestProto.optionalCordExtension)); + + // ----------------------------------------------------------------- + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed32Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed64Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFloatExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedDoubleExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBoolExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBytesExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedGroupExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignMessageExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportEnumExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringPieceExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedCordExtension)); + + assertEqualsExactType(201 , message.getExtension(UnittestProto.repeatedInt32Extension , 0)); + assertEqualsExactType(202L , message.getExtension(UnittestProto.repeatedInt64Extension , 0)); + assertEqualsExactType(203 , message.getExtension(UnittestProto.repeatedUint32Extension , 0)); + assertEqualsExactType(204L , message.getExtension(UnittestProto.repeatedUint64Extension , 0)); + assertEqualsExactType(205 , message.getExtension(UnittestProto.repeatedSint32Extension , 0)); + assertEqualsExactType(206L , message.getExtension(UnittestProto.repeatedSint64Extension , 0)); + assertEqualsExactType(207 , message.getExtension(UnittestProto.repeatedFixed32Extension , 0)); + assertEqualsExactType(208L , message.getExtension(UnittestProto.repeatedFixed64Extension , 0)); + assertEqualsExactType(209 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 0)); + assertEqualsExactType(210L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 0)); + assertEqualsExactType(211F , message.getExtension(UnittestProto.repeatedFloatExtension , 0)); + assertEqualsExactType(212D , message.getExtension(UnittestProto.repeatedDoubleExtension , 0)); + assertEqualsExactType(true , message.getExtension(UnittestProto.repeatedBoolExtension , 0)); + assertEqualsExactType("215", message.getExtension(UnittestProto.repeatedStringExtension , 0)); + assertEqualsExactType(toBytes("216"), message.getExtension(UnittestProto.repeatedBytesExtension, 0)); + + assertEqualsExactType(217, message.getExtension(UnittestProto.repeatedGroupExtension , 0).getA()); + assertEqualsExactType(218, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 0).getBb()); + assertEqualsExactType(219, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 0).getC()); + assertEqualsExactType(220, message.getExtension(UnittestProto.repeatedImportMessageExtension , 0).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 0)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAR, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 0)); + assertEqualsExactType(ImportEnum.IMPORT_BAR, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 0)); + + assertEqualsExactType("224", message.getExtension(UnittestProto.repeatedStringPieceExtension, 0)); + assertEqualsExactType("225", message.getExtension(UnittestProto.repeatedCordExtension, 0)); + + assertEqualsExactType(301 , message.getExtension(UnittestProto.repeatedInt32Extension , 1)); + assertEqualsExactType(302L , message.getExtension(UnittestProto.repeatedInt64Extension , 1)); + assertEqualsExactType(303 , message.getExtension(UnittestProto.repeatedUint32Extension , 1)); + assertEqualsExactType(304L , message.getExtension(UnittestProto.repeatedUint64Extension , 1)); + assertEqualsExactType(305 , message.getExtension(UnittestProto.repeatedSint32Extension , 1)); + assertEqualsExactType(306L , message.getExtension(UnittestProto.repeatedSint64Extension , 1)); + assertEqualsExactType(307 , message.getExtension(UnittestProto.repeatedFixed32Extension , 1)); + assertEqualsExactType(308L , message.getExtension(UnittestProto.repeatedFixed64Extension , 1)); + assertEqualsExactType(309 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 1)); + assertEqualsExactType(310L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 1)); + assertEqualsExactType(311F , message.getExtension(UnittestProto.repeatedFloatExtension , 1)); + assertEqualsExactType(312D , message.getExtension(UnittestProto.repeatedDoubleExtension , 1)); + assertEqualsExactType(false, message.getExtension(UnittestProto.repeatedBoolExtension , 1)); + assertEqualsExactType("315", message.getExtension(UnittestProto.repeatedStringExtension , 1)); + assertEqualsExactType(toBytes("316"), message.getExtension(UnittestProto.repeatedBytesExtension, 1)); + + assertEqualsExactType(317, message.getExtension(UnittestProto.repeatedGroupExtension , 1).getA()); + assertEqualsExactType(318, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 1).getBb()); + assertEqualsExactType(319, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 1).getC()); + assertEqualsExactType(320, message.getExtension(UnittestProto.repeatedImportMessageExtension , 1).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAZ, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 1)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAZ, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 1)); + assertEqualsExactType(ImportEnum.IMPORT_BAZ, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 1)); + + assertEqualsExactType("324", message.getExtension(UnittestProto.repeatedStringPieceExtension, 1)); + assertEqualsExactType("325", message.getExtension(UnittestProto.repeatedCordExtension, 1)); + + // ----------------------------------------------------------------- + + Assert.assertTrue(message.hasExtension(UnittestProto.defaultInt32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultInt64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultUint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultUint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultFixed32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultFixed64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSfixed32Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSfixed64Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultFloatExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultDoubleExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultBoolExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultStringExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultBytesExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.defaultNestedEnumExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultForeignEnumExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultImportEnumExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.defaultStringPieceExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultCordExtension)); + + assertEqualsExactType(401 , message.getExtension(UnittestProto.defaultInt32Extension )); + assertEqualsExactType(402L , message.getExtension(UnittestProto.defaultInt64Extension )); + assertEqualsExactType(403 , message.getExtension(UnittestProto.defaultUint32Extension )); + assertEqualsExactType(404L , message.getExtension(UnittestProto.defaultUint64Extension )); + assertEqualsExactType(405 , message.getExtension(UnittestProto.defaultSint32Extension )); + assertEqualsExactType(406L , message.getExtension(UnittestProto.defaultSint64Extension )); + assertEqualsExactType(407 , message.getExtension(UnittestProto.defaultFixed32Extension )); + assertEqualsExactType(408L , message.getExtension(UnittestProto.defaultFixed64Extension )); + assertEqualsExactType(409 , message.getExtension(UnittestProto.defaultSfixed32Extension)); + assertEqualsExactType(410L , message.getExtension(UnittestProto.defaultSfixed64Extension)); + assertEqualsExactType(411F , message.getExtension(UnittestProto.defaultFloatExtension )); + assertEqualsExactType(412D , message.getExtension(UnittestProto.defaultDoubleExtension )); + assertEqualsExactType(false, message.getExtension(UnittestProto.defaultBoolExtension )); + assertEqualsExactType("415", message.getExtension(UnittestProto.defaultStringExtension )); + assertEqualsExactType(toBytes("416"), message.getExtension(UnittestProto.defaultBytesExtension)); + + assertEqualsExactType(TestAllTypes.NestedEnum.FOO, + message.getExtension(UnittestProto.defaultNestedEnumExtension )); + assertEqualsExactType(ForeignEnum.FOREIGN_FOO, + message.getExtension(UnittestProto.defaultForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_FOO, + message.getExtension(UnittestProto.defaultImportEnumExtension)); + + assertEqualsExactType("424", message.getExtension(UnittestProto.defaultStringPieceExtension)); + assertEqualsExactType("425", message.getExtension(UnittestProto.defaultCordExtension)); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all extensions of + * {@code message} are cleared, and that getting the extensions returns their + * default values. + */ + public static void assertExtensionsClear(TestAllExtensions message) { + // hasBlah() should initially be false for all optional fields. + Assert.assertFalse(message.hasExtension(UnittestProto.optionalInt32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalInt64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalUint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalUint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalFixed32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalFixed64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSfixed32Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSfixed64Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalFloatExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalDoubleExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalBoolExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalStringExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalBytesExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.optionalGroupExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalNestedMessageExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalForeignMessageExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalImportMessageExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.optionalNestedEnumExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalForeignEnumExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalImportEnumExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.optionalStringPieceExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalCordExtension)); + + // Optional fields without defaults are set to zero or something like it. + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalInt32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalInt64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalUint32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalUint64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalSint32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalSint64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalFixed32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalFixed64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalSfixed32Extension)); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalSfixed64Extension)); + assertEqualsExactType(0F , message.getExtension(UnittestProto.optionalFloatExtension )); + assertEqualsExactType(0D , message.getExtension(UnittestProto.optionalDoubleExtension )); + assertEqualsExactType(false, message.getExtension(UnittestProto.optionalBoolExtension )); + assertEqualsExactType("" , message.getExtension(UnittestProto.optionalStringExtension )); + assertEqualsExactType(ByteString.EMPTY, message.getExtension(UnittestProto.optionalBytesExtension)); + + // Embedded messages should also be clear. + Assert.assertFalse(message.getExtension(UnittestProto.optionalGroupExtension ).hasA()); + Assert.assertFalse(message.getExtension(UnittestProto.optionalNestedMessageExtension ).hasBb()); + Assert.assertFalse(message.getExtension(UnittestProto.optionalForeignMessageExtension).hasC()); + Assert.assertFalse(message.getExtension(UnittestProto.optionalImportMessageExtension ).hasD()); + + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalGroupExtension ).getA()); + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalNestedMessageExtension ).getBb()); + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalForeignMessageExtension).getC()); + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalImportMessageExtension ).getD()); + + // Enums without defaults are set to the first value in the enum. + assertEqualsExactType(TestAllTypes.NestedEnum.FOO, + message.getExtension(UnittestProto.optionalNestedEnumExtension )); + assertEqualsExactType(ForeignEnum.FOREIGN_FOO, + message.getExtension(UnittestProto.optionalForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_FOO, + message.getExtension(UnittestProto.optionalImportEnumExtension)); + + assertEqualsExactType("", message.getExtension(UnittestProto.optionalStringPieceExtension)); + assertEqualsExactType("", message.getExtension(UnittestProto.optionalCordExtension)); + + // Repeated fields are empty. + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedInt32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedInt64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedUint32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedUint64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSint32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSint64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedFixed32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedFixed64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSfixed32Extension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSfixed64Extension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedFloatExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedDoubleExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedBoolExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedStringExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedBytesExtension )); + + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedGroupExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedNestedMessageExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedForeignMessageExtension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedImportMessageExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedNestedEnumExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedForeignEnumExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedImportEnumExtension )); + + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedStringPieceExtension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedCordExtension)); + + // hasBlah() should also be false for all default fields. + Assert.assertFalse(message.hasExtension(UnittestProto.defaultInt32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultInt64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultUint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultUint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultFixed32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultFixed64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSfixed32Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSfixed64Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultFloatExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultDoubleExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultBoolExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultStringExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultBytesExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.defaultNestedEnumExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultForeignEnumExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultImportEnumExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.defaultStringPieceExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultCordExtension)); + + // Fields with defaults have their default values (duh). + assertEqualsExactType( 41 , message.getExtension(UnittestProto.defaultInt32Extension )); + assertEqualsExactType( 42L , message.getExtension(UnittestProto.defaultInt64Extension )); + assertEqualsExactType( 43 , message.getExtension(UnittestProto.defaultUint32Extension )); + assertEqualsExactType( 44L , message.getExtension(UnittestProto.defaultUint64Extension )); + assertEqualsExactType(-45 , message.getExtension(UnittestProto.defaultSint32Extension )); + assertEqualsExactType( 46L , message.getExtension(UnittestProto.defaultSint64Extension )); + assertEqualsExactType( 47 , message.getExtension(UnittestProto.defaultFixed32Extension )); + assertEqualsExactType( 48L , message.getExtension(UnittestProto.defaultFixed64Extension )); + assertEqualsExactType( 49 , message.getExtension(UnittestProto.defaultSfixed32Extension)); + assertEqualsExactType(-50L , message.getExtension(UnittestProto.defaultSfixed64Extension)); + assertEqualsExactType( 51.5F , message.getExtension(UnittestProto.defaultFloatExtension )); + assertEqualsExactType( 52e3D , message.getExtension(UnittestProto.defaultDoubleExtension )); + assertEqualsExactType(true , message.getExtension(UnittestProto.defaultBoolExtension )); + assertEqualsExactType("hello", message.getExtension(UnittestProto.defaultStringExtension )); + assertEqualsExactType(toBytes("world"), message.getExtension(UnittestProto.defaultBytesExtension)); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.defaultNestedEnumExtension )); + assertEqualsExactType(ForeignEnum.FOREIGN_BAR, + message.getExtension(UnittestProto.defaultForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_BAR, + message.getExtension(UnittestProto.defaultImportEnumExtension)); + + assertEqualsExactType("abc", message.getExtension(UnittestProto.defaultStringPieceExtension)); + assertEqualsExactType("123", message.getExtension(UnittestProto.defaultCordExtension)); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all extensions of + * {@code message} are set to the values assigned by {@code setAllExtensions} + * followed by {@code modifyRepeatedExtensions}. + */ + public static void assertRepeatedExtensionsModified( + TestAllExtensions message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed32Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed64Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFloatExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedDoubleExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBoolExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBytesExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedGroupExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignMessageExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportEnumExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringPieceExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedCordExtension)); + + assertEqualsExactType(201 , message.getExtension(UnittestProto.repeatedInt32Extension , 0)); + assertEqualsExactType(202L , message.getExtension(UnittestProto.repeatedInt64Extension , 0)); + assertEqualsExactType(203 , message.getExtension(UnittestProto.repeatedUint32Extension , 0)); + assertEqualsExactType(204L , message.getExtension(UnittestProto.repeatedUint64Extension , 0)); + assertEqualsExactType(205 , message.getExtension(UnittestProto.repeatedSint32Extension , 0)); + assertEqualsExactType(206L , message.getExtension(UnittestProto.repeatedSint64Extension , 0)); + assertEqualsExactType(207 , message.getExtension(UnittestProto.repeatedFixed32Extension , 0)); + assertEqualsExactType(208L , message.getExtension(UnittestProto.repeatedFixed64Extension , 0)); + assertEqualsExactType(209 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 0)); + assertEqualsExactType(210L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 0)); + assertEqualsExactType(211F , message.getExtension(UnittestProto.repeatedFloatExtension , 0)); + assertEqualsExactType(212D , message.getExtension(UnittestProto.repeatedDoubleExtension , 0)); + assertEqualsExactType(true , message.getExtension(UnittestProto.repeatedBoolExtension , 0)); + assertEqualsExactType("215", message.getExtension(UnittestProto.repeatedStringExtension , 0)); + assertEqualsExactType(toBytes("216"), message.getExtension(UnittestProto.repeatedBytesExtension, 0)); + + assertEqualsExactType(217, message.getExtension(UnittestProto.repeatedGroupExtension , 0).getA()); + assertEqualsExactType(218, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 0).getBb()); + assertEqualsExactType(219, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 0).getC()); + assertEqualsExactType(220, message.getExtension(UnittestProto.repeatedImportMessageExtension , 0).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 0)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAR, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 0)); + assertEqualsExactType(ImportEnum.IMPORT_BAR, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 0)); + + assertEqualsExactType("224", message.getExtension(UnittestProto.repeatedStringPieceExtension, 0)); + assertEqualsExactType("225", message.getExtension(UnittestProto.repeatedCordExtension, 0)); + + // Actually verify the second (modified) elements now. + assertEqualsExactType(501 , message.getExtension(UnittestProto.repeatedInt32Extension , 1)); + assertEqualsExactType(502L , message.getExtension(UnittestProto.repeatedInt64Extension , 1)); + assertEqualsExactType(503 , message.getExtension(UnittestProto.repeatedUint32Extension , 1)); + assertEqualsExactType(504L , message.getExtension(UnittestProto.repeatedUint64Extension , 1)); + assertEqualsExactType(505 , message.getExtension(UnittestProto.repeatedSint32Extension , 1)); + assertEqualsExactType(506L , message.getExtension(UnittestProto.repeatedSint64Extension , 1)); + assertEqualsExactType(507 , message.getExtension(UnittestProto.repeatedFixed32Extension , 1)); + assertEqualsExactType(508L , message.getExtension(UnittestProto.repeatedFixed64Extension , 1)); + assertEqualsExactType(509 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 1)); + assertEqualsExactType(510L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 1)); + assertEqualsExactType(511F , message.getExtension(UnittestProto.repeatedFloatExtension , 1)); + assertEqualsExactType(512D , message.getExtension(UnittestProto.repeatedDoubleExtension , 1)); + assertEqualsExactType(true , message.getExtension(UnittestProto.repeatedBoolExtension , 1)); + assertEqualsExactType("515", message.getExtension(UnittestProto.repeatedStringExtension , 1)); + assertEqualsExactType(toBytes("516"), message.getExtension(UnittestProto.repeatedBytesExtension, 1)); + + assertEqualsExactType(517, message.getExtension(UnittestProto.repeatedGroupExtension , 1).getA()); + assertEqualsExactType(518, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 1).getBb()); + assertEqualsExactType(519, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 1).getC()); + assertEqualsExactType(520, message.getExtension(UnittestProto.repeatedImportMessageExtension , 1).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.FOO, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 1)); + assertEqualsExactType(ForeignEnum.FOREIGN_FOO, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 1)); + assertEqualsExactType(ImportEnum.IMPORT_FOO, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 1)); + + assertEqualsExactType("524", message.getExtension(UnittestProto.repeatedStringPieceExtension, 1)); + assertEqualsExactType("525", message.getExtension(UnittestProto.repeatedCordExtension, 1)); + } + + // =================================================================== + + /** + * Performs the same things that the methods of {@code TestUtil} do, but + * via the reflection interface. This is its own class because it needs + * to know what descriptor to use. + */ + public static class ReflectionTester { + private final Descriptors.Descriptor baseDescriptor; + private final ExtensionRegistry extensionRegistry; + + private final Descriptors.FileDescriptor file; + private final Descriptors.FileDescriptor importFile; + + private final Descriptors.Descriptor optionalGroup; + private final Descriptors.Descriptor repeatedGroup; + private final Descriptors.Descriptor nestedMessage; + private final Descriptors.Descriptor foreignMessage; + private final Descriptors.Descriptor importMessage; + + private final Descriptors.FieldDescriptor groupA; + private final Descriptors.FieldDescriptor repeatedGroupA; + private final Descriptors.FieldDescriptor nestedB; + private final Descriptors.FieldDescriptor foreignC; + private final Descriptors.FieldDescriptor importD; + + private final Descriptors.EnumDescriptor nestedEnum; + private final Descriptors.EnumDescriptor foreignEnum; + private final Descriptors.EnumDescriptor importEnum; + + private final Descriptors.EnumValueDescriptor nestedFoo; + private final Descriptors.EnumValueDescriptor nestedBar; + private final Descriptors.EnumValueDescriptor nestedBaz; + private final Descriptors.EnumValueDescriptor foreignFoo; + private final Descriptors.EnumValueDescriptor foreignBar; + private final Descriptors.EnumValueDescriptor foreignBaz; + private final Descriptors.EnumValueDescriptor importFoo; + private final Descriptors.EnumValueDescriptor importBar; + private final Descriptors.EnumValueDescriptor importBaz; + + /** + * Construct a {@code ReflectionTester} that will expect messages using + * the given descriptor. + * + * Normally {@code baseDescriptor} should be a descriptor for the type + * {@code TestAllTypes}, defined in + * {@code google/protobuf/unittest.proto}. However, if + * {@code extensionRegistry} is non-null, then {@code baseDescriptor} should + * be for {@code TestAllExtensions} instead, and instead of reading and + * writing normal fields, the tester will read and write extensions. + * All of {@code TestAllExtensions}' extensions must be registered in the + * registry. + */ + public ReflectionTester(Descriptors.Descriptor baseDescriptor, + ExtensionRegistry extensionRegistry) { + this.baseDescriptor = baseDescriptor; + this.extensionRegistry = extensionRegistry; + + this.file = baseDescriptor.getFile(); + Assert.assertEquals(1, file.getDependencies().size()); + this.importFile = file.getDependencies().get(0); + + Descriptors.Descriptor testAllTypes; + if (extensionRegistry == null) { + testAllTypes = baseDescriptor; + } else { + testAllTypes = file.findMessageTypeByName("TestAllTypes"); + Assert.assertNotNull(testAllTypes); + } + + if (extensionRegistry == null) { + this.optionalGroup = + baseDescriptor.findNestedTypeByName("OptionalGroup"); + this.repeatedGroup = + baseDescriptor.findNestedTypeByName("RepeatedGroup"); + } else { + this.optionalGroup = + file.findMessageTypeByName("OptionalGroup_extension"); + this.repeatedGroup = + file.findMessageTypeByName("RepeatedGroup_extension"); + } + this.nestedMessage = testAllTypes.findNestedTypeByName("NestedMessage"); + this.foreignMessage = file.findMessageTypeByName("ForeignMessage"); + this.importMessage = importFile.findMessageTypeByName("ImportMessage"); + + this.nestedEnum = testAllTypes.findEnumTypeByName("NestedEnum"); + this.foreignEnum = file.findEnumTypeByName("ForeignEnum"); + this.importEnum = importFile.findEnumTypeByName("ImportEnum"); + + Assert.assertNotNull(optionalGroup ); + Assert.assertNotNull(repeatedGroup ); + Assert.assertNotNull(nestedMessage ); + Assert.assertNotNull(foreignMessage); + Assert.assertNotNull(importMessage ); + Assert.assertNotNull(nestedEnum ); + Assert.assertNotNull(foreignEnum ); + Assert.assertNotNull(importEnum ); + + this.nestedB = nestedMessage .findFieldByName("bb"); + this.foreignC = foreignMessage.findFieldByName("c"); + this.importD = importMessage .findFieldByName("d"); + this.nestedFoo = nestedEnum.findValueByName("FOO"); + this.nestedBar = nestedEnum.findValueByName("BAR"); + this.nestedBaz = nestedEnum.findValueByName("BAZ"); + this.foreignFoo = foreignEnum.findValueByName("FOREIGN_FOO"); + this.foreignBar = foreignEnum.findValueByName("FOREIGN_BAR"); + this.foreignBaz = foreignEnum.findValueByName("FOREIGN_BAZ"); + this.importFoo = importEnum.findValueByName("IMPORT_FOO"); + this.importBar = importEnum.findValueByName("IMPORT_BAR"); + this.importBaz = importEnum.findValueByName("IMPORT_BAZ"); + + this.groupA = optionalGroup.findFieldByName("a"); + this.repeatedGroupA = repeatedGroup.findFieldByName("a"); + + Assert.assertNotNull(groupA ); + Assert.assertNotNull(repeatedGroupA); + Assert.assertNotNull(nestedB ); + Assert.assertNotNull(foreignC ); + Assert.assertNotNull(importD ); + Assert.assertNotNull(nestedFoo ); + Assert.assertNotNull(nestedBar ); + Assert.assertNotNull(nestedBaz ); + Assert.assertNotNull(foreignFoo ); + Assert.assertNotNull(foreignBar ); + Assert.assertNotNull(foreignBaz ); + Assert.assertNotNull(importFoo ); + Assert.assertNotNull(importBar ); + Assert.assertNotNull(importBaz ); + } + + /** + * Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes. + */ + private Descriptors.FieldDescriptor f(String name) { + Descriptors.FieldDescriptor result; + if (extensionRegistry == null) { + result = baseDescriptor.findFieldByName(name); + } else { + result = file.findExtensionByName(name + "_extension"); + } + Assert.assertNotNull(result); + return result; + } + + /** + * Calls {@code parent.newBuilderForField()} or uses the + * {@code ExtensionRegistry} to find an appropriate builder, depending + * on what type is being tested. + */ + private Message.Builder newBuilderForField( + Message.Builder parent, Descriptors.FieldDescriptor field) { + if (extensionRegistry == null) { + return parent.newBuilderForField(field); + } else { + ExtensionRegistry.ExtensionInfo extension = + extensionRegistry.findExtensionByNumber(field.getContainingType(), + field.getNumber()); + Assert.assertNotNull(extension); + Assert.assertNotNull(extension.defaultInstance); + return extension.defaultInstance.newBuilderForType(); + } + } + + // ------------------------------------------------------------------- + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllFieldsSet()}, using the {@link Message.Builder} + * reflection interface. + */ + void setAllFieldsViaReflection(Message.Builder message) { + message.setField(f("optional_int32" ), 101 ); + message.setField(f("optional_int64" ), 102L); + message.setField(f("optional_uint32" ), 103 ); + message.setField(f("optional_uint64" ), 104L); + message.setField(f("optional_sint32" ), 105 ); + message.setField(f("optional_sint64" ), 106L); + message.setField(f("optional_fixed32" ), 107 ); + message.setField(f("optional_fixed64" ), 108L); + message.setField(f("optional_sfixed32"), 109 ); + message.setField(f("optional_sfixed64"), 110L); + message.setField(f("optional_float" ), 111F); + message.setField(f("optional_double" ), 112D); + message.setField(f("optional_bool" ), true); + message.setField(f("optional_string" ), "115"); + message.setField(f("optional_bytes" ), toBytes("116")); + + message.setField(f("optionalgroup"), + newBuilderForField(message, f("optionalgroup")) + .setField(groupA, 117).build()); + message.setField(f("optional_nested_message"), + newBuilderForField(message, f("optional_nested_message")) + .setField(nestedB, 118).build()); + message.setField(f("optional_foreign_message"), + newBuilderForField(message, f("optional_foreign_message")) + .setField(foreignC, 119).build()); + message.setField(f("optional_import_message"), + newBuilderForField(message, f("optional_import_message")) + .setField(importD, 120).build()); + + message.setField(f("optional_nested_enum" ), nestedBaz); + message.setField(f("optional_foreign_enum"), foreignBaz); + message.setField(f("optional_import_enum" ), importBaz); + + message.setField(f("optional_string_piece" ), "124"); + message.setField(f("optional_cord" ), "125"); + + // ----------------------------------------------------------------- + + message.addRepeatedField(f("repeated_int32" ), 201 ); + message.addRepeatedField(f("repeated_int64" ), 202L); + message.addRepeatedField(f("repeated_uint32" ), 203 ); + message.addRepeatedField(f("repeated_uint64" ), 204L); + message.addRepeatedField(f("repeated_sint32" ), 205 ); + message.addRepeatedField(f("repeated_sint64" ), 206L); + message.addRepeatedField(f("repeated_fixed32" ), 207 ); + message.addRepeatedField(f("repeated_fixed64" ), 208L); + message.addRepeatedField(f("repeated_sfixed32"), 209 ); + message.addRepeatedField(f("repeated_sfixed64"), 210L); + message.addRepeatedField(f("repeated_float" ), 211F); + message.addRepeatedField(f("repeated_double" ), 212D); + message.addRepeatedField(f("repeated_bool" ), true); + message.addRepeatedField(f("repeated_string" ), "215"); + message.addRepeatedField(f("repeated_bytes" ), toBytes("216")); + + message.addRepeatedField(f("repeatedgroup"), + newBuilderForField(message, f("repeatedgroup")) + .setField(repeatedGroupA, 217).build()); + message.addRepeatedField(f("repeated_nested_message"), + newBuilderForField(message, f("repeated_nested_message")) + .setField(nestedB, 218).build()); + message.addRepeatedField(f("repeated_foreign_message"), + newBuilderForField(message, f("repeated_foreign_message")) + .setField(foreignC, 219).build()); + message.addRepeatedField(f("repeated_import_message"), + newBuilderForField(message, f("repeated_import_message")) + .setField(importD, 220).build()); + + message.addRepeatedField(f("repeated_nested_enum" ), nestedBar); + message.addRepeatedField(f("repeated_foreign_enum"), foreignBar); + message.addRepeatedField(f("repeated_import_enum" ), importBar); + + message.addRepeatedField(f("repeated_string_piece" ), "224"); + message.addRepeatedField(f("repeated_cord" ), "225"); + + // Add a second one of each field. + message.addRepeatedField(f("repeated_int32" ), 301 ); + message.addRepeatedField(f("repeated_int64" ), 302L); + message.addRepeatedField(f("repeated_uint32" ), 303 ); + message.addRepeatedField(f("repeated_uint64" ), 304L); + message.addRepeatedField(f("repeated_sint32" ), 305 ); + message.addRepeatedField(f("repeated_sint64" ), 306L); + message.addRepeatedField(f("repeated_fixed32" ), 307 ); + message.addRepeatedField(f("repeated_fixed64" ), 308L); + message.addRepeatedField(f("repeated_sfixed32"), 309 ); + message.addRepeatedField(f("repeated_sfixed64"), 310L); + message.addRepeatedField(f("repeated_float" ), 311F); + message.addRepeatedField(f("repeated_double" ), 312D); + message.addRepeatedField(f("repeated_bool" ), false); + message.addRepeatedField(f("repeated_string" ), "315"); + message.addRepeatedField(f("repeated_bytes" ), toBytes("316")); + + message.addRepeatedField(f("repeatedgroup"), + newBuilderForField(message, f("repeatedgroup")) + .setField(repeatedGroupA, 317).build()); + message.addRepeatedField(f("repeated_nested_message"), + newBuilderForField(message, f("repeated_nested_message")) + .setField(nestedB, 318).build()); + message.addRepeatedField(f("repeated_foreign_message"), + newBuilderForField(message, f("repeated_foreign_message")) + .setField(foreignC, 319).build()); + message.addRepeatedField(f("repeated_import_message"), + newBuilderForField(message, f("repeated_import_message")) + .setField(importD, 320).build()); + + message.addRepeatedField(f("repeated_nested_enum" ), nestedBaz); + message.addRepeatedField(f("repeated_foreign_enum"), foreignBaz); + message.addRepeatedField(f("repeated_import_enum" ), importBaz); + + message.addRepeatedField(f("repeated_string_piece" ), "324"); + message.addRepeatedField(f("repeated_cord" ), "325"); + + // ----------------------------------------------------------------- + + message.setField(f("default_int32" ), 401 ); + message.setField(f("default_int64" ), 402L); + message.setField(f("default_uint32" ), 403 ); + message.setField(f("default_uint64" ), 404L); + message.setField(f("default_sint32" ), 405 ); + message.setField(f("default_sint64" ), 406L); + message.setField(f("default_fixed32" ), 407 ); + message.setField(f("default_fixed64" ), 408L); + message.setField(f("default_sfixed32"), 409 ); + message.setField(f("default_sfixed64"), 410L); + message.setField(f("default_float" ), 411F); + message.setField(f("default_double" ), 412D); + message.setField(f("default_bool" ), false); + message.setField(f("default_string" ), "415"); + message.setField(f("default_bytes" ), toBytes("416")); + + message.setField(f("default_nested_enum" ), nestedFoo); + message.setField(f("default_foreign_enum"), foreignFoo); + message.setField(f("default_import_enum" ), importFoo); + + message.setField(f("default_string_piece" ), "424"); + message.setField(f("default_cord" ), "425"); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated fields of {@code message} to contain the values + * expected by {@code assertRepeatedFieldsModified()}, using the + * {@link Message.Builder} reflection interface. + */ + void modifyRepeatedFieldsViaReflection(Message.Builder message) { + message.setRepeatedField(f("repeated_int32" ), 1, 501 ); + message.setRepeatedField(f("repeated_int64" ), 1, 502L); + message.setRepeatedField(f("repeated_uint32" ), 1, 503 ); + message.setRepeatedField(f("repeated_uint64" ), 1, 504L); + message.setRepeatedField(f("repeated_sint32" ), 1, 505 ); + message.setRepeatedField(f("repeated_sint64" ), 1, 506L); + message.setRepeatedField(f("repeated_fixed32" ), 1, 507 ); + message.setRepeatedField(f("repeated_fixed64" ), 1, 508L); + message.setRepeatedField(f("repeated_sfixed32"), 1, 509 ); + message.setRepeatedField(f("repeated_sfixed64"), 1, 510L); + message.setRepeatedField(f("repeated_float" ), 1, 511F); + message.setRepeatedField(f("repeated_double" ), 1, 512D); + message.setRepeatedField(f("repeated_bool" ), 1, true); + message.setRepeatedField(f("repeated_string" ), 1, "515"); + message.setRepeatedField(f("repeated_bytes" ), 1, toBytes("516")); + + message.setRepeatedField(f("repeatedgroup"), 1, + newBuilderForField(message, f("repeatedgroup")) + .setField(repeatedGroupA, 517).build()); + message.setRepeatedField(f("repeated_nested_message"), 1, + newBuilderForField(message, f("repeated_nested_message")) + .setField(nestedB, 518).build()); + message.setRepeatedField(f("repeated_foreign_message"), 1, + newBuilderForField(message, f("repeated_foreign_message")) + .setField(foreignC, 519).build()); + message.setRepeatedField(f("repeated_import_message"), 1, + newBuilderForField(message, f("repeated_import_message")) + .setField(importD, 520).build()); + + message.setRepeatedField(f("repeated_nested_enum" ), 1, nestedFoo); + message.setRepeatedField(f("repeated_foreign_enum"), 1, foreignFoo); + message.setRepeatedField(f("repeated_import_enum" ), 1, importFoo); + + message.setRepeatedField(f("repeated_string_piece"), 1, "524"); + message.setRepeatedField(f("repeated_cord"), 1, "525"); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields}, + * using the {@link Message} reflection interface. + */ + public void assertAllFieldsSetViaReflection(Message message) { + Assert.assertTrue(message.hasField(f("optional_int32" ))); + Assert.assertTrue(message.hasField(f("optional_int64" ))); + Assert.assertTrue(message.hasField(f("optional_uint32" ))); + Assert.assertTrue(message.hasField(f("optional_uint64" ))); + Assert.assertTrue(message.hasField(f("optional_sint32" ))); + Assert.assertTrue(message.hasField(f("optional_sint64" ))); + Assert.assertTrue(message.hasField(f("optional_fixed32" ))); + Assert.assertTrue(message.hasField(f("optional_fixed64" ))); + Assert.assertTrue(message.hasField(f("optional_sfixed32"))); + Assert.assertTrue(message.hasField(f("optional_sfixed64"))); + Assert.assertTrue(message.hasField(f("optional_float" ))); + Assert.assertTrue(message.hasField(f("optional_double" ))); + Assert.assertTrue(message.hasField(f("optional_bool" ))); + Assert.assertTrue(message.hasField(f("optional_string" ))); + Assert.assertTrue(message.hasField(f("optional_bytes" ))); + + Assert.assertTrue(message.hasField(f("optionalgroup" ))); + Assert.assertTrue(message.hasField(f("optional_nested_message" ))); + Assert.assertTrue(message.hasField(f("optional_foreign_message"))); + Assert.assertTrue(message.hasField(f("optional_import_message" ))); + + Assert.assertTrue( + ((Message)message.getField(f("optionalgroup"))).hasField(groupA)); + Assert.assertTrue( + ((Message)message.getField(f("optional_nested_message"))) + .hasField(nestedB)); + Assert.assertTrue( + ((Message)message.getField(f("optional_foreign_message"))) + .hasField(foreignC)); + Assert.assertTrue( + ((Message)message.getField(f("optional_import_message"))) + .hasField(importD)); + + Assert.assertTrue(message.hasField(f("optional_nested_enum" ))); + Assert.assertTrue(message.hasField(f("optional_foreign_enum"))); + Assert.assertTrue(message.hasField(f("optional_import_enum" ))); + + Assert.assertTrue(message.hasField(f("optional_string_piece"))); + Assert.assertTrue(message.hasField(f("optional_cord"))); + + Assert.assertEquals(101 , message.getField(f("optional_int32" ))); + Assert.assertEquals(102L , message.getField(f("optional_int64" ))); + Assert.assertEquals(103 , message.getField(f("optional_uint32" ))); + Assert.assertEquals(104L , message.getField(f("optional_uint64" ))); + Assert.assertEquals(105 , message.getField(f("optional_sint32" ))); + Assert.assertEquals(106L , message.getField(f("optional_sint64" ))); + Assert.assertEquals(107 , message.getField(f("optional_fixed32" ))); + Assert.assertEquals(108L , message.getField(f("optional_fixed64" ))); + Assert.assertEquals(109 , message.getField(f("optional_sfixed32"))); + Assert.assertEquals(110L , message.getField(f("optional_sfixed64"))); + Assert.assertEquals(111F , message.getField(f("optional_float" ))); + Assert.assertEquals(112D , message.getField(f("optional_double" ))); + Assert.assertEquals(true , message.getField(f("optional_bool" ))); + Assert.assertEquals("115", message.getField(f("optional_string" ))); + Assert.assertEquals(toBytes("116"), message.getField(f("optional_bytes"))); + + Assert.assertEquals(117, + ((Message)message.getField(f("optionalgroup"))).getField(groupA)); + Assert.assertEquals(118, + ((Message)message.getField(f("optional_nested_message"))) + .getField(nestedB)); + Assert.assertEquals(119, + ((Message)message.getField(f("optional_foreign_message"))) + .getField(foreignC)); + Assert.assertEquals(120, + ((Message)message.getField(f("optional_import_message"))) + .getField(importD)); + + Assert.assertEquals( nestedBaz, message.getField(f("optional_nested_enum" ))); + Assert.assertEquals(foreignBaz, message.getField(f("optional_foreign_enum"))); + Assert.assertEquals( importBaz, message.getField(f("optional_import_enum" ))); + + Assert.assertEquals("124", message.getField(f("optional_string_piece"))); + Assert.assertEquals("125", message.getField(f("optional_cord"))); + + // ----------------------------------------------------------------- + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed32"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed64"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_float" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_double" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bool" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bytes" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeatedgroup" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_message"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_enum" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string_piece"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_cord"))); + + Assert.assertEquals(201 , message.getRepeatedField(f("repeated_int32" ), 0)); + Assert.assertEquals(202L , message.getRepeatedField(f("repeated_int64" ), 0)); + Assert.assertEquals(203 , message.getRepeatedField(f("repeated_uint32" ), 0)); + Assert.assertEquals(204L , message.getRepeatedField(f("repeated_uint64" ), 0)); + Assert.assertEquals(205 , message.getRepeatedField(f("repeated_sint32" ), 0)); + Assert.assertEquals(206L , message.getRepeatedField(f("repeated_sint64" ), 0)); + Assert.assertEquals(207 , message.getRepeatedField(f("repeated_fixed32" ), 0)); + Assert.assertEquals(208L , message.getRepeatedField(f("repeated_fixed64" ), 0)); + Assert.assertEquals(209 , message.getRepeatedField(f("repeated_sfixed32"), 0)); + Assert.assertEquals(210L , message.getRepeatedField(f("repeated_sfixed64"), 0)); + Assert.assertEquals(211F , message.getRepeatedField(f("repeated_float" ), 0)); + Assert.assertEquals(212D , message.getRepeatedField(f("repeated_double" ), 0)); + Assert.assertEquals(true , message.getRepeatedField(f("repeated_bool" ), 0)); + Assert.assertEquals("215", message.getRepeatedField(f("repeated_string" ), 0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedField(f("repeated_bytes"), 0)); + + Assert.assertEquals(217, + ((Message)message.getRepeatedField(f("repeatedgroup"), 0)) + .getField(repeatedGroupA)); + Assert.assertEquals(218, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 0)) + .getField(nestedB)); + Assert.assertEquals(219, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 0)) + .getField(foreignC)); + Assert.assertEquals(220, + ((Message)message.getRepeatedField(f("repeated_import_message"), 0)) + .getField(importD)); + + Assert.assertEquals( nestedBar, message.getRepeatedField(f("repeated_nested_enum" ),0)); + Assert.assertEquals(foreignBar, message.getRepeatedField(f("repeated_foreign_enum"),0)); + Assert.assertEquals( importBar, message.getRepeatedField(f("repeated_import_enum" ),0)); + + Assert.assertEquals("224", message.getRepeatedField(f("repeated_string_piece"), 0)); + Assert.assertEquals("225", message.getRepeatedField(f("repeated_cord"), 0)); + + Assert.assertEquals(301 , message.getRepeatedField(f("repeated_int32" ), 1)); + Assert.assertEquals(302L , message.getRepeatedField(f("repeated_int64" ), 1)); + Assert.assertEquals(303 , message.getRepeatedField(f("repeated_uint32" ), 1)); + Assert.assertEquals(304L , message.getRepeatedField(f("repeated_uint64" ), 1)); + Assert.assertEquals(305 , message.getRepeatedField(f("repeated_sint32" ), 1)); + Assert.assertEquals(306L , message.getRepeatedField(f("repeated_sint64" ), 1)); + Assert.assertEquals(307 , message.getRepeatedField(f("repeated_fixed32" ), 1)); + Assert.assertEquals(308L , message.getRepeatedField(f("repeated_fixed64" ), 1)); + Assert.assertEquals(309 , message.getRepeatedField(f("repeated_sfixed32"), 1)); + Assert.assertEquals(310L , message.getRepeatedField(f("repeated_sfixed64"), 1)); + Assert.assertEquals(311F , message.getRepeatedField(f("repeated_float" ), 1)); + Assert.assertEquals(312D , message.getRepeatedField(f("repeated_double" ), 1)); + Assert.assertEquals(false, message.getRepeatedField(f("repeated_bool" ), 1)); + Assert.assertEquals("315", message.getRepeatedField(f("repeated_string" ), 1)); + Assert.assertEquals(toBytes("316"), message.getRepeatedField(f("repeated_bytes"), 1)); + + Assert.assertEquals(317, + ((Message)message.getRepeatedField(f("repeatedgroup"), 1)) + .getField(repeatedGroupA)); + Assert.assertEquals(318, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 1)) + .getField(nestedB)); + Assert.assertEquals(319, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 1)) + .getField(foreignC)); + Assert.assertEquals(320, + ((Message)message.getRepeatedField(f("repeated_import_message"), 1)) + .getField(importD)); + + Assert.assertEquals( nestedBaz, message.getRepeatedField(f("repeated_nested_enum" ),1)); + Assert.assertEquals(foreignBaz, message.getRepeatedField(f("repeated_foreign_enum"),1)); + Assert.assertEquals( importBaz, message.getRepeatedField(f("repeated_import_enum" ),1)); + + Assert.assertEquals("324", message.getRepeatedField(f("repeated_string_piece"), 1)); + Assert.assertEquals("325", message.getRepeatedField(f("repeated_cord"), 1)); + + // ----------------------------------------------------------------- + + Assert.assertTrue(message.hasField(f("default_int32" ))); + Assert.assertTrue(message.hasField(f("default_int64" ))); + Assert.assertTrue(message.hasField(f("default_uint32" ))); + Assert.assertTrue(message.hasField(f("default_uint64" ))); + Assert.assertTrue(message.hasField(f("default_sint32" ))); + Assert.assertTrue(message.hasField(f("default_sint64" ))); + Assert.assertTrue(message.hasField(f("default_fixed32" ))); + Assert.assertTrue(message.hasField(f("default_fixed64" ))); + Assert.assertTrue(message.hasField(f("default_sfixed32"))); + Assert.assertTrue(message.hasField(f("default_sfixed64"))); + Assert.assertTrue(message.hasField(f("default_float" ))); + Assert.assertTrue(message.hasField(f("default_double" ))); + Assert.assertTrue(message.hasField(f("default_bool" ))); + Assert.assertTrue(message.hasField(f("default_string" ))); + Assert.assertTrue(message.hasField(f("default_bytes" ))); + + Assert.assertTrue(message.hasField(f("default_nested_enum" ))); + Assert.assertTrue(message.hasField(f("default_foreign_enum"))); + Assert.assertTrue(message.hasField(f("default_import_enum" ))); + + Assert.assertTrue(message.hasField(f("default_string_piece"))); + Assert.assertTrue(message.hasField(f("default_cord"))); + + Assert.assertEquals(401 , message.getField(f("default_int32" ))); + Assert.assertEquals(402L , message.getField(f("default_int64" ))); + Assert.assertEquals(403 , message.getField(f("default_uint32" ))); + Assert.assertEquals(404L , message.getField(f("default_uint64" ))); + Assert.assertEquals(405 , message.getField(f("default_sint32" ))); + Assert.assertEquals(406L , message.getField(f("default_sint64" ))); + Assert.assertEquals(407 , message.getField(f("default_fixed32" ))); + Assert.assertEquals(408L , message.getField(f("default_fixed64" ))); + Assert.assertEquals(409 , message.getField(f("default_sfixed32"))); + Assert.assertEquals(410L , message.getField(f("default_sfixed64"))); + Assert.assertEquals(411F , message.getField(f("default_float" ))); + Assert.assertEquals(412D , message.getField(f("default_double" ))); + Assert.assertEquals(false, message.getField(f("default_bool" ))); + Assert.assertEquals("415", message.getField(f("default_string" ))); + Assert.assertEquals(toBytes("416"), message.getField(f("default_bytes"))); + + Assert.assertEquals( nestedFoo, message.getField(f("default_nested_enum" ))); + Assert.assertEquals(foreignFoo, message.getField(f("default_foreign_enum"))); + Assert.assertEquals( importFoo, message.getField(f("default_import_enum" ))); + + Assert.assertEquals("424", message.getField(f("default_string_piece"))); + Assert.assertEquals("425", message.getField(f("default_cord"))); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are cleared, and that getting the fields returns their + * default values, using the {@link Message} reflection interface. + */ + public void assertClearViaReflection(Message message) { + // has_blah() should initially be false for all optional fields. + Assert.assertFalse(message.hasField(f("optional_int32" ))); + Assert.assertFalse(message.hasField(f("optional_int64" ))); + Assert.assertFalse(message.hasField(f("optional_uint32" ))); + Assert.assertFalse(message.hasField(f("optional_uint64" ))); + Assert.assertFalse(message.hasField(f("optional_sint32" ))); + Assert.assertFalse(message.hasField(f("optional_sint64" ))); + Assert.assertFalse(message.hasField(f("optional_fixed32" ))); + Assert.assertFalse(message.hasField(f("optional_fixed64" ))); + Assert.assertFalse(message.hasField(f("optional_sfixed32"))); + Assert.assertFalse(message.hasField(f("optional_sfixed64"))); + Assert.assertFalse(message.hasField(f("optional_float" ))); + Assert.assertFalse(message.hasField(f("optional_double" ))); + Assert.assertFalse(message.hasField(f("optional_bool" ))); + Assert.assertFalse(message.hasField(f("optional_string" ))); + Assert.assertFalse(message.hasField(f("optional_bytes" ))); + + Assert.assertFalse(message.hasField(f("optionalgroup" ))); + Assert.assertFalse(message.hasField(f("optional_nested_message" ))); + Assert.assertFalse(message.hasField(f("optional_foreign_message"))); + Assert.assertFalse(message.hasField(f("optional_import_message" ))); + + Assert.assertFalse(message.hasField(f("optional_nested_enum" ))); + Assert.assertFalse(message.hasField(f("optional_foreign_enum"))); + Assert.assertFalse(message.hasField(f("optional_import_enum" ))); + + Assert.assertFalse(message.hasField(f("optional_string_piece"))); + Assert.assertFalse(message.hasField(f("optional_cord"))); + + // Optional fields without defaults are set to zero or something like it. + Assert.assertEquals(0 , message.getField(f("optional_int32" ))); + Assert.assertEquals(0L , message.getField(f("optional_int64" ))); + Assert.assertEquals(0 , message.getField(f("optional_uint32" ))); + Assert.assertEquals(0L , message.getField(f("optional_uint64" ))); + Assert.assertEquals(0 , message.getField(f("optional_sint32" ))); + Assert.assertEquals(0L , message.getField(f("optional_sint64" ))); + Assert.assertEquals(0 , message.getField(f("optional_fixed32" ))); + Assert.assertEquals(0L , message.getField(f("optional_fixed64" ))); + Assert.assertEquals(0 , message.getField(f("optional_sfixed32"))); + Assert.assertEquals(0L , message.getField(f("optional_sfixed64"))); + Assert.assertEquals(0F , message.getField(f("optional_float" ))); + Assert.assertEquals(0D , message.getField(f("optional_double" ))); + Assert.assertEquals(false, message.getField(f("optional_bool" ))); + Assert.assertEquals("" , message.getField(f("optional_string" ))); + Assert.assertEquals(ByteString.EMPTY, message.getField(f("optional_bytes"))); + + // Embedded messages should also be clear. + Assert.assertFalse( + ((Message)message.getField(f("optionalgroup"))).hasField(groupA)); + Assert.assertFalse( + ((Message)message.getField(f("optional_nested_message"))) + .hasField(nestedB)); + Assert.assertFalse( + ((Message)message.getField(f("optional_foreign_message"))) + .hasField(foreignC)); + Assert.assertFalse( + ((Message)message.getField(f("optional_import_message"))) + .hasField(importD)); + + Assert.assertEquals(0, + ((Message)message.getField(f("optionalgroup"))).getField(groupA)); + Assert.assertEquals(0, + ((Message)message.getField(f("optional_nested_message"))) + .getField(nestedB)); + Assert.assertEquals(0, + ((Message)message.getField(f("optional_foreign_message"))) + .getField(foreignC)); + Assert.assertEquals(0, + ((Message)message.getField(f("optional_import_message"))) + .getField(importD)); + + // Enums without defaults are set to the first value in the enum. + Assert.assertEquals( nestedFoo, message.getField(f("optional_nested_enum" ))); + Assert.assertEquals(foreignFoo, message.getField(f("optional_foreign_enum"))); + Assert.assertEquals( importFoo, message.getField(f("optional_import_enum" ))); + + Assert.assertEquals("", message.getField(f("optional_string_piece"))); + Assert.assertEquals("", message.getField(f("optional_cord"))); + + // Repeated fields are empty. + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_int32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_int64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_uint32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_uint64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sint32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sint64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_fixed32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_fixed64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sfixed32"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sfixed64"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_float" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_double" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_bool" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_string" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_bytes" ))); + + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeatedgroup" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_nested_message" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_foreign_message"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_import_message" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_nested_enum" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_foreign_enum" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_import_enum" ))); + + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_string_piece"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_cord"))); + + // has_blah() should also be false for all default fields. + Assert.assertFalse(message.hasField(f("default_int32" ))); + Assert.assertFalse(message.hasField(f("default_int64" ))); + Assert.assertFalse(message.hasField(f("default_uint32" ))); + Assert.assertFalse(message.hasField(f("default_uint64" ))); + Assert.assertFalse(message.hasField(f("default_sint32" ))); + Assert.assertFalse(message.hasField(f("default_sint64" ))); + Assert.assertFalse(message.hasField(f("default_fixed32" ))); + Assert.assertFalse(message.hasField(f("default_fixed64" ))); + Assert.assertFalse(message.hasField(f("default_sfixed32"))); + Assert.assertFalse(message.hasField(f("default_sfixed64"))); + Assert.assertFalse(message.hasField(f("default_float" ))); + Assert.assertFalse(message.hasField(f("default_double" ))); + Assert.assertFalse(message.hasField(f("default_bool" ))); + Assert.assertFalse(message.hasField(f("default_string" ))); + Assert.assertFalse(message.hasField(f("default_bytes" ))); + + Assert.assertFalse(message.hasField(f("default_nested_enum" ))); + Assert.assertFalse(message.hasField(f("default_foreign_enum"))); + Assert.assertFalse(message.hasField(f("default_import_enum" ))); + + Assert.assertFalse(message.hasField(f("default_string_piece" ))); + Assert.assertFalse(message.hasField(f("default_cord" ))); + + // Fields with defaults have their default values (duh). + Assert.assertEquals( 41 , message.getField(f("default_int32" ))); + Assert.assertEquals( 42L , message.getField(f("default_int64" ))); + Assert.assertEquals( 43 , message.getField(f("default_uint32" ))); + Assert.assertEquals( 44L , message.getField(f("default_uint64" ))); + Assert.assertEquals(-45 , message.getField(f("default_sint32" ))); + Assert.assertEquals( 46L , message.getField(f("default_sint64" ))); + Assert.assertEquals( 47 , message.getField(f("default_fixed32" ))); + Assert.assertEquals( 48L , message.getField(f("default_fixed64" ))); + Assert.assertEquals( 49 , message.getField(f("default_sfixed32"))); + Assert.assertEquals(-50L , message.getField(f("default_sfixed64"))); + Assert.assertEquals( 51.5F , message.getField(f("default_float" ))); + Assert.assertEquals( 52e3D , message.getField(f("default_double" ))); + Assert.assertEquals(true , message.getField(f("default_bool" ))); + Assert.assertEquals("hello", message.getField(f("default_string" ))); + Assert.assertEquals(toBytes("world"), message.getField(f("default_bytes"))); + + Assert.assertEquals( nestedBar, message.getField(f("default_nested_enum" ))); + Assert.assertEquals(foreignBar, message.getField(f("default_foreign_enum"))); + Assert.assertEquals( importBar, message.getField(f("default_import_enum" ))); + + Assert.assertEquals("abc", message.getField(f("default_string_piece"))); + Assert.assertEquals("123", message.getField(f("default_cord"))); + } + + // --------------------------------------------------------------- + + public void assertRepeatedFieldsModifiedViaReflection(Message message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed32"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed64"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_float" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_double" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bool" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bytes" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeatedgroup" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_message"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_enum" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string_piece"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_cord"))); + + Assert.assertEquals(201 , message.getRepeatedField(f("repeated_int32" ), 0)); + Assert.assertEquals(202L , message.getRepeatedField(f("repeated_int64" ), 0)); + Assert.assertEquals(203 , message.getRepeatedField(f("repeated_uint32" ), 0)); + Assert.assertEquals(204L , message.getRepeatedField(f("repeated_uint64" ), 0)); + Assert.assertEquals(205 , message.getRepeatedField(f("repeated_sint32" ), 0)); + Assert.assertEquals(206L , message.getRepeatedField(f("repeated_sint64" ), 0)); + Assert.assertEquals(207 , message.getRepeatedField(f("repeated_fixed32" ), 0)); + Assert.assertEquals(208L , message.getRepeatedField(f("repeated_fixed64" ), 0)); + Assert.assertEquals(209 , message.getRepeatedField(f("repeated_sfixed32"), 0)); + Assert.assertEquals(210L , message.getRepeatedField(f("repeated_sfixed64"), 0)); + Assert.assertEquals(211F , message.getRepeatedField(f("repeated_float" ), 0)); + Assert.assertEquals(212D , message.getRepeatedField(f("repeated_double" ), 0)); + Assert.assertEquals(true , message.getRepeatedField(f("repeated_bool" ), 0)); + Assert.assertEquals("215", message.getRepeatedField(f("repeated_string" ), 0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedField(f("repeated_bytes"), 0)); + + Assert.assertEquals(217, + ((Message)message.getRepeatedField(f("repeatedgroup"), 0)) + .getField(repeatedGroupA)); + Assert.assertEquals(218, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 0)) + .getField(nestedB)); + Assert.assertEquals(219, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 0)) + .getField(foreignC)); + Assert.assertEquals(220, + ((Message)message.getRepeatedField(f("repeated_import_message"), 0)) + .getField(importD)); + + Assert.assertEquals( nestedBar, message.getRepeatedField(f("repeated_nested_enum" ),0)); + Assert.assertEquals(foreignBar, message.getRepeatedField(f("repeated_foreign_enum"),0)); + Assert.assertEquals( importBar, message.getRepeatedField(f("repeated_import_enum" ),0)); + + Assert.assertEquals("224", message.getRepeatedField(f("repeated_string_piece"), 0)); + Assert.assertEquals("225", message.getRepeatedField(f("repeated_cord"), 0)); + + Assert.assertEquals(501 , message.getRepeatedField(f("repeated_int32" ), 1)); + Assert.assertEquals(502L , message.getRepeatedField(f("repeated_int64" ), 1)); + Assert.assertEquals(503 , message.getRepeatedField(f("repeated_uint32" ), 1)); + Assert.assertEquals(504L , message.getRepeatedField(f("repeated_uint64" ), 1)); + Assert.assertEquals(505 , message.getRepeatedField(f("repeated_sint32" ), 1)); + Assert.assertEquals(506L , message.getRepeatedField(f("repeated_sint64" ), 1)); + Assert.assertEquals(507 , message.getRepeatedField(f("repeated_fixed32" ), 1)); + Assert.assertEquals(508L , message.getRepeatedField(f("repeated_fixed64" ), 1)); + Assert.assertEquals(509 , message.getRepeatedField(f("repeated_sfixed32"), 1)); + Assert.assertEquals(510L , message.getRepeatedField(f("repeated_sfixed64"), 1)); + Assert.assertEquals(511F , message.getRepeatedField(f("repeated_float" ), 1)); + Assert.assertEquals(512D , message.getRepeatedField(f("repeated_double" ), 1)); + Assert.assertEquals(true , message.getRepeatedField(f("repeated_bool" ), 1)); + Assert.assertEquals("515", message.getRepeatedField(f("repeated_string" ), 1)); + Assert.assertEquals(toBytes("516"), message.getRepeatedField(f("repeated_bytes"), 1)); + + Assert.assertEquals(517, + ((Message)message.getRepeatedField(f("repeatedgroup"), 1)) + .getField(repeatedGroupA)); + Assert.assertEquals(518, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 1)) + .getField(nestedB)); + Assert.assertEquals(519, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 1)) + .getField(foreignC)); + Assert.assertEquals(520, + ((Message)message.getRepeatedField(f("repeated_import_message"), 1)) + .getField(importD)); + + Assert.assertEquals( nestedFoo, message.getRepeatedField(f("repeated_nested_enum" ),1)); + Assert.assertEquals(foreignFoo, message.getRepeatedField(f("repeated_foreign_enum"),1)); + Assert.assertEquals( importFoo, message.getRepeatedField(f("repeated_import_enum" ),1)); + + Assert.assertEquals("524", message.getRepeatedField(f("repeated_string_piece"), 1)); + Assert.assertEquals("525", message.getRepeatedField(f("repeated_cord"), 1)); + } + } + + /** + * @param filePath The path relative to + * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + */ + public static String readTextFromFile(String filePath) { + return readBytesFromFile(filePath).toStringUtf8(); + } + + private static File getTestDataDir() { + // Search each parent directory looking for "src/google/protobuf". + File ancestor = new File("."); + try { + ancestor = ancestor.getCanonicalFile(); + } catch (IOException e) { + throw new RuntimeException( + "Couldn't get canonical name of working directory.", e); + } + while (ancestor != null && ancestor.exists()) { + if (new File(ancestor, "src/google/protobuf").exists()) { + return new File(ancestor, "src/google/protobuf/testdata"); + } + ancestor = ancestor.getParentFile(); + } + + throw new RuntimeException( + "Could not find golden files. This test must be run from within the " + + "protobuf source package so that it can read test data files from the " + + "C++ source tree."); + } + + /** + * @param filePath The path relative to + * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + */ + public static ByteString readBytesFromFile(String filename) { + File fullPath = new File(getTestDataDir(), filename); + try { + RandomAccessFile file = new RandomAccessFile(fullPath, "r"); + byte[] content = new byte[(int) file.length()]; + file.readFully(content); + return ByteString.copyFrom(content); + } catch (IOException e) { + // Throw a RuntimeException here so that we can call this function from + // static initializers. + throw new IllegalArgumentException( + "Couldn't read file: " + fullPath.getPath(), e); + } + } + + /** + * Get the bytes of the "golden message". This is a serialized TestAllTypes + * with all fields set as they would be by + * {@link setAllFields(TestAllTypes.Builder)}, but it is loaded from a file + * on disk rather than generated dynamically. The file is actually generated + * by C++ code, so testing against it verifies compatibility with C++. + */ + public static ByteString getGoldenMessage() { + if (goldenMessage == null) { + goldenMessage = readBytesFromFile("golden_message"); + } + return goldenMessage; + } + private static ByteString goldenMessage = null; +} diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java new file mode 100644 index 00000000..2c485c55 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -0,0 +1,534 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestEmptyMessage; +import protobuf_unittest.UnittestMset.TestMessageSet; +import protobuf_unittest.UnittestMset.TestMessageSetExtension1; +import protobuf_unittest.UnittestMset.TestMessageSetExtension2; + +import junit.framework.TestCase; + +import java.io.StringReader; + +/** + * Test case for {@link TextFormat}. + * + * TODO(wenboz): ExtensionTest and rest of text_format_unittest.cc. + * + * @author wenboz@google.com (Wenbo Zhu) + */ +public class TextFormatTest extends TestCase { + + // A basic string with different escapable characters for testing. + private final static String kEscapeTestString = + "\"A string with ' characters \n and \r newlines and \t tabs and \001 " + + "slashes \\"; + + // A representation of the above string with all the characters escaped. + private final static String kEscapeTestStringEscaped = + "\"\\\"A string with \\' characters \\n and \\r newlines " + + "and \\t tabs and \\001 slashes \\\\\""; + + private static String allFieldsSetText = TestUtil.readTextFromFile( + "text_format_unittest_data.txt"); + private static String allExtensionsSetText = TestUtil.readTextFromFile( + "text_format_unittest_extensions_data.txt"); + + private String exoticText = + "repeated_int32: -1\n" + + "repeated_int32: -2147483648\n" + + "repeated_int64: -1\n" + + "repeated_int64: -9223372036854775808\n" + + "repeated_uint32: 4294967295\n" + + "repeated_uint32: 2147483648\n" + + "repeated_uint64: 18446744073709551615\n" + + "repeated_uint64: 9223372036854775808\n" + + "repeated_double: 123.0\n" + + "repeated_double: 123.5\n" + + "repeated_double: 0.125\n" + + "repeated_double: 1.23E17\n" + + "repeated_double: 1.235E22\n" + + "repeated_double: 1.235E-18\n" + + "repeated_double: 123.456789\n" + + "repeated_double: Infinity\n" + + "repeated_double: -Infinity\n" + + "repeated_double: NaN\n" + + "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"" + + "\\341\\210\\264\"\n" + + "repeated_bytes: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\376\"\n"; + + private String messageSetText = + "[protobuf_unittest.TestMessageSetExtension1] {\n" + + " i: 123\n" + + "}\n" + + "[protobuf_unittest.TestMessageSetExtension2] {\n" + + " str: \"foo\"\n" + + "}\n"; + + /** Print TestAllTypes and compare with golden file. */ + public void testPrintMessage() throws Exception { + String javaText = TextFormat.printToString(TestUtil.getAllSet()); + + // Java likes to add a trailing ".0" to floats and doubles. C printf + // (with %g format) does not. Our golden files are used for both + // C++ and Java TextFormat classes, so we need to conform. + javaText = javaText.replace(".0\n", "\n"); + + assertEquals(allFieldsSetText, javaText); + } + + /** Print TestAllExtensions and compare with golden file. */ + public void testPrintExtensions() throws Exception { + String javaText = TextFormat.printToString(TestUtil.getAllExtensionsSet()); + + // Java likes to add a trailing ".0" to floats and doubles. C printf + // (with %g format) does not. Our golden files are used for both + // C++ and Java TextFormat classes, so we need to conform. + javaText = javaText.replace(".0\n", "\n"); + + assertEquals(allExtensionsSetText, javaText); + } + + public void testPrintUnknownFields() throws Exception { + // Test printing of unknown fields in a message. + + TestEmptyMessage message = + TestEmptyMessage.newBuilder() + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(5, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addFixed32(2) + .addFixed64(3) + .addLengthDelimited(ByteString.copyFromUtf8("4")) + .addGroup( + UnknownFieldSet.newBuilder() + .addField(10, + UnknownFieldSet.Field.newBuilder() + .addVarint(5) + .build()) + .build()) + .build()) + .addField(8, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addVarint(2) + .addVarint(3) + .build()) + .addField(15, + UnknownFieldSet.Field.newBuilder() + .addVarint(0xABCDEF1234567890L) + .addFixed32(0xABCD1234) + .addFixed64(0xABCDEF1234567890L) + .build()) + .build()) + .build(); + + assertEquals( + "5: 1\n" + + "5: 0x00000002\n" + + "5: 0x0000000000000003\n" + + "5: \"4\"\n" + + "5 {\n" + + " 10: 5\n" + + "}\n" + + "8: 1\n" + + "8: 2\n" + + "8: 3\n" + + "15: 12379813812177893520\n" + + "15: 0xabcd1234\n" + + "15: 0xabcdef1234567890\n", + TextFormat.printToString(message)); + } + + /** + * Helper to construct a ByteString from a String containing only 8-bit + * characters. The characters are converted directly to bytes, *not* + * encoded using UTF-8. + */ + private ByteString bytes(String str) throws Exception { + return ByteString.copyFrom(str.getBytes("ISO-8859-1")); + } + + /** + * Helper to construct a ByteString from a bunch of bytes. The inputs are + * actually ints so that I can use hex notation and not get stupid errors + * about precision. + */ + private ByteString bytes(int... bytesAsInts) { + byte[] bytes = new byte[bytesAsInts.length]; + for (int i = 0; i < bytesAsInts.length; i++) { + bytes[i] = (byte) bytesAsInts[i]; + } + return ByteString.copyFrom(bytes); + } + + public void testPrintExotic() throws Exception { + Message message = TestAllTypes.newBuilder() + // Signed vs. unsigned numbers. + .addRepeatedInt32 (-1) + .addRepeatedUint32(-1) + .addRepeatedInt64 (-1) + .addRepeatedUint64(-1) + + .addRepeatedInt32 (1 << 31) + .addRepeatedUint32(1 << 31) + .addRepeatedInt64 (1l << 63) + .addRepeatedUint64(1l << 63) + + // Floats of various precisions and exponents. + .addRepeatedDouble(123) + .addRepeatedDouble(123.5) + .addRepeatedDouble(0.125) + .addRepeatedDouble(123e15) + .addRepeatedDouble(123.5e20) + .addRepeatedDouble(123.5e-20) + .addRepeatedDouble(123.456789) + .addRepeatedDouble(Double.POSITIVE_INFINITY) + .addRepeatedDouble(Double.NEGATIVE_INFINITY) + .addRepeatedDouble(Double.NaN) + + // Strings and bytes that needing escaping. + .addRepeatedString("\0\001\007\b\f\n\r\t\013\\\'\"\u1234") + .addRepeatedBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\u00fe")) + .build(); + + assertEquals(exoticText, message.toString()); + } + + public void testPrintMessageSet() throws Exception { + TestMessageSet messageSet = + TestMessageSet.newBuilder() + .setExtension( + TestMessageSetExtension1.messageSetExtension, + TestMessageSetExtension1.newBuilder().setI(123).build()) + .setExtension( + TestMessageSetExtension2.messageSetExtension, + TestMessageSetExtension2.newBuilder().setStr("foo").build()) + .build(); + + assertEquals(messageSetText, messageSet.toString()); + } + + // ================================================================= + + public void testParse() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(allFieldsSetText, builder); + TestUtil.assertAllFieldsSet(builder.build()); + } + + public void testParseReader() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(new StringReader(allFieldsSetText), builder); + TestUtil.assertAllFieldsSet(builder.build()); + } + + public void testParseExtensions() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TextFormat.merge(allExtensionsSetText, + TestUtil.getExtensionRegistry(), + builder); + TestUtil.assertAllExtensionsSet(builder.build()); + } + + public void testParseExotic() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(exoticText, builder); + + // Too lazy to check things individually. Don't try to debug this + // if testPrintExotic() is failing. + assertEquals(exoticText, builder.build().toString()); + } + + public void testParseMessageSet() throws Exception { + ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); + extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); + extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); + + TestMessageSet.Builder builder = TestMessageSet.newBuilder(); + TextFormat.merge(messageSetText, extensionRegistry, builder); + TestMessageSet messageSet = builder.build(); + + assertTrue(messageSet.hasExtension( + TestMessageSetExtension1.messageSetExtension)); + assertEquals(123, messageSet.getExtension( + TestMessageSetExtension1.messageSetExtension).getI()); + assertTrue(messageSet.hasExtension( + TestMessageSetExtension2.messageSetExtension)); + assertEquals("foo", messageSet.getExtension( + TestMessageSetExtension2.messageSetExtension).getStr()); + } + + public void testParseNumericEnum() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge("optional_nested_enum: 2", builder); + assertEquals(TestAllTypes.NestedEnum.BAR, builder.getOptionalNestedEnum()); + } + + public void testParseAngleBrackets() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge("OptionalGroup: < a: 1 >", builder); + assertTrue(builder.hasOptionalGroup()); + assertEquals(1, builder.getOptionalGroup().getA()); + } + + private void assertParseError(String error, String text) { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + try { + TextFormat.merge(text, TestUtil.getExtensionRegistry(), builder); + fail("Expected parse exception."); + } catch (TextFormat.ParseException e) { + assertEquals(error, e.getMessage()); + } + } + + public void testParseErrors() throws Exception { + assertParseError( + "1:16: Expected \":\".", + "optional_int32 123"); + assertParseError( + "1:23: Expected identifier.", + "optional_nested_enum: ?"); + assertParseError( + "1:18: Couldn't parse integer: Number must be positive: -1", + "optional_uint32: -1"); + assertParseError( + "1:17: Couldn't parse integer: Number out of range for 32-bit signed " + + "integer: 82301481290849012385230157", + "optional_int32: 82301481290849012385230157"); + assertParseError( + "1:16: Expected \"true\" or \"false\".", + "optional_bool: maybe"); + assertParseError( + "1:18: Expected string.", + "optional_string: 123"); + assertParseError( + "1:18: String missing ending quote.", + "optional_string: \"ueoauaoe"); + assertParseError( + "1:18: String missing ending quote.", + "optional_string: \"ueoauaoe\n" + + "optional_int32: 123"); + assertParseError( + "1:18: Invalid escape sequence: '\\z'", + "optional_string: \"\\z\""); + assertParseError( + "1:18: String missing ending quote.", + "optional_string: \"ueoauaoe\n" + + "optional_int32: 123"); + assertParseError( + "1:2: Extension \"nosuchext\" not found in the ExtensionRegistry.", + "[nosuchext]: 123"); + assertParseError( + "1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " + + "not extend message type \"protobuf_unittest.TestAllTypes\".", + "[protobuf_unittest.optional_int32_extension]: 123"); + assertParseError( + "1:1: Message type \"protobuf_unittest.TestAllTypes\" has no field " + + "named \"nosuchfield\".", + "nosuchfield: 123"); + assertParseError( + "1:21: Expected \">\".", + "OptionalGroup < a: 1"); + assertParseError( + "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + + "value named \"NO_SUCH_VALUE\".", + "optional_nested_enum: NO_SUCH_VALUE"); + assertParseError( + "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + + "value with number 123.", + "optional_nested_enum: 123"); + + // Delimiters must match. + assertParseError( + "1:22: Expected identifier.", + "OptionalGroup < a: 1 }"); + assertParseError( + "1:22: Expected identifier.", + "OptionalGroup { a: 1 >"); + } + + // ================================================================= + + public void testEscape() throws Exception { + // Escape sequences. + assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"", + TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\""))); + assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"", + TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"")); + assertEquals(bytes("\0\001\007\b\f\n\r\t\013\\\'\""), + TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); + assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"", + TextFormat.unescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); + + // Unicode handling. + assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234")); + assertEquals("\\341\\210\\264", + TextFormat.escapeBytes(bytes(0xe1, 0x88, 0xb4))); + assertEquals("\u1234", TextFormat.unescapeText("\\341\\210\\264")); + assertEquals(bytes(0xe1, 0x88, 0xb4), + TextFormat.unescapeBytes("\\341\\210\\264")); + assertEquals("\u1234", TextFormat.unescapeText("\\xe1\\x88\\xb4")); + assertEquals(bytes(0xe1, 0x88, 0xb4), + TextFormat.unescapeBytes("\\xe1\\x88\\xb4")); + + // Errors. + try { + TextFormat.unescapeText("\\x"); + fail("Should have thrown an exception."); + } catch (TextFormat.InvalidEscapeSequence e) { + // success + } + + try { + TextFormat.unescapeText("\\z"); + fail("Should have thrown an exception."); + } catch (TextFormat.InvalidEscapeSequence e) { + // success + } + + try { + TextFormat.unescapeText("\\"); + fail("Should have thrown an exception."); + } catch (TextFormat.InvalidEscapeSequence e) { + // success + } + } + + public void testParseInteger() throws Exception { + assertEquals( 0, TextFormat.parseInt32( "0")); + assertEquals( 1, TextFormat.parseInt32( "1")); + assertEquals( -1, TextFormat.parseInt32( "-1")); + assertEquals( 12345, TextFormat.parseInt32( "12345")); + assertEquals( -12345, TextFormat.parseInt32( "-12345")); + assertEquals( 2147483647, TextFormat.parseInt32( "2147483647")); + assertEquals(-2147483648, TextFormat.parseInt32("-2147483648")); + + assertEquals( 0, TextFormat.parseUInt32( "0")); + assertEquals( 1, TextFormat.parseUInt32( "1")); + assertEquals( 12345, TextFormat.parseUInt32( "12345")); + assertEquals( 2147483647, TextFormat.parseUInt32("2147483647")); + assertEquals((int) 2147483648L, TextFormat.parseUInt32("2147483648")); + assertEquals((int) 4294967295L, TextFormat.parseUInt32("4294967295")); + + assertEquals( 0L, TextFormat.parseInt64( "0")); + assertEquals( 1L, TextFormat.parseInt64( "1")); + assertEquals( -1L, TextFormat.parseInt64( "-1")); + assertEquals( 12345L, TextFormat.parseInt64( "12345")); + assertEquals( -12345L, TextFormat.parseInt64( "-12345")); + assertEquals( 2147483647L, TextFormat.parseInt64( "2147483647")); + assertEquals(-2147483648L, TextFormat.parseInt64("-2147483648")); + assertEquals( 4294967295L, TextFormat.parseInt64( "4294967295")); + assertEquals( 4294967296L, TextFormat.parseInt64( "4294967296")); + assertEquals(9223372036854775807L, + TextFormat.parseInt64("9223372036854775807")); + assertEquals(-9223372036854775808L, + TextFormat.parseInt64("-9223372036854775808")); + + assertEquals( 0L, TextFormat.parseUInt64( "0")); + assertEquals( 1L, TextFormat.parseUInt64( "1")); + assertEquals( 12345L, TextFormat.parseUInt64( "12345")); + assertEquals( 2147483647L, TextFormat.parseUInt64( "2147483647")); + assertEquals( 4294967295L, TextFormat.parseUInt64( "4294967295")); + assertEquals( 4294967296L, TextFormat.parseUInt64( "4294967296")); + assertEquals(9223372036854775807L, + TextFormat.parseUInt64("9223372036854775807")); + assertEquals(-9223372036854775808L, + TextFormat.parseUInt64("9223372036854775808")); + assertEquals(-1L, TextFormat.parseUInt64("18446744073709551615")); + + // Hex + assertEquals(0x1234abcd, TextFormat.parseInt32("0x1234abcd")); + assertEquals(-0x1234abcd, TextFormat.parseInt32("-0x1234abcd")); + assertEquals(-1, TextFormat.parseUInt64("0xffffffffffffffff")); + assertEquals(0x7fffffffffffffffL, + TextFormat.parseInt64("0x7fffffffffffffff")); + + // Octal + assertEquals(01234567, TextFormat.parseInt32("01234567")); + + // Out-of-range + try { + TextFormat.parseInt32("2147483648"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseInt32("-2147483649"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt32("4294967296"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt32("-1"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseInt64("9223372036854775808"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseInt64("-9223372036854775809"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt64("18446744073709551616"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt64("-1"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + // Not a number. + try { + TextFormat.parseInt32("abcd"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + } +} diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java new file mode 100644 index 00000000..0ad2683d --- /dev/null +++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java @@ -0,0 +1,315 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestEmptyMessage; +import protobuf_unittest.UnittestProto. + TestEmptyMessageWithExtensions; + +import junit.framework.TestCase; +import java.util.Arrays; +import java.util.Map; + +/** + * Tests related to unknown field handling. + * + * @author kenton@google.com (Kenton Varda) + */ +public class UnknownFieldSetTest extends TestCase { + public void setUp() throws Exception { + descriptor = TestAllTypes.getDescriptor(); + allFields = TestUtil.getAllSet(); + allFieldsData = allFields.toByteString(); + emptyMessage = TestEmptyMessage.parseFrom(allFieldsData); + unknownFields = emptyMessage.getUnknownFields(); + } + + UnknownFieldSet.Field getField(String name) { + Descriptors.FieldDescriptor field = descriptor.findFieldByName(name); + assertNotNull(field); + return unknownFields.getField(field.getNumber()); + } + + // Constructs a protocol buffer which contains fields with all the same + // numbers as allFieldsData except that each field is some other wire + // type. + ByteString getBizarroData() throws Exception { + UnknownFieldSet.Builder bizarroFields = UnknownFieldSet.newBuilder(); + + UnknownFieldSet.Field varintField = + UnknownFieldSet.Field.newBuilder().addVarint(1).build(); + UnknownFieldSet.Field fixed32Field = + UnknownFieldSet.Field.newBuilder().addFixed32(1).build(); + + for (Map.Entry entry : + unknownFields.asMap().entrySet()) { + if (entry.getValue().getVarintList().isEmpty()) { + // Original field is not a varint, so use a varint. + bizarroFields.addField(entry.getKey(), varintField); + } else { + // Original field *is* a varint, so use something else. + bizarroFields.addField(entry.getKey(), fixed32Field); + } + } + + return bizarroFields.build().toByteString(); + } + + Descriptors.Descriptor descriptor; + TestAllTypes allFields; + ByteString allFieldsData; + + // An empty message that has been parsed from allFieldsData. So, it has + // unknown fields of every type. + TestEmptyMessage emptyMessage; + UnknownFieldSet unknownFields; + + // ================================================================= + + public void testVarint() throws Exception { + UnknownFieldSet.Field field = getField("optional_int32"); + assertEquals(1, field.getVarintList().size()); + assertEquals(allFields.getOptionalInt32(), + (long) field.getVarintList().get(0)); + } + + public void testFixed32() throws Exception { + UnknownFieldSet.Field field = getField("optional_fixed32"); + assertEquals(1, field.getFixed32List().size()); + assertEquals(allFields.getOptionalFixed32(), + (int) field.getFixed32List().get(0)); + } + + public void testFixed64() throws Exception { + UnknownFieldSet.Field field = getField("optional_fixed64"); + assertEquals(1, field.getFixed64List().size()); + assertEquals(allFields.getOptionalFixed64(), + (long) field.getFixed64List().get(0)); + } + + public void testLengthDelimited() throws Exception { + UnknownFieldSet.Field field = getField("optional_bytes"); + assertEquals(1, field.getLengthDelimitedList().size()); + assertEquals(allFields.getOptionalBytes(), + field.getLengthDelimitedList().get(0)); + } + + public void testGroup() throws Exception { + Descriptors.FieldDescriptor nestedFieldDescriptor = + TestAllTypes.OptionalGroup.getDescriptor().findFieldByName("a"); + assertNotNull(nestedFieldDescriptor); + + UnknownFieldSet.Field field = getField("optionalgroup"); + assertEquals(1, field.getGroupList().size()); + + UnknownFieldSet group = field.getGroupList().get(0); + assertEquals(1, group.asMap().size()); + assertTrue(group.hasField(nestedFieldDescriptor.getNumber())); + + UnknownFieldSet.Field nestedField = + group.getField(nestedFieldDescriptor.getNumber()); + assertEquals(1, nestedField.getVarintList().size()); + assertEquals(allFields.getOptionalGroup().getA(), + (long) nestedField.getVarintList().get(0)); + } + + public void testSerialize() throws Exception { + // Check that serializing the UnknownFieldSet produces the original data + // again. + ByteString data = emptyMessage.toByteString(); + assertEquals(allFieldsData, data); + } + + public void testCopyFrom() throws Exception { + TestEmptyMessage message = + TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).build(); + + assertEquals(emptyMessage.toString(), message.toString()); + } + + public void testMergeFrom() throws Exception { + TestEmptyMessage source = + TestEmptyMessage.newBuilder() + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(2, + UnknownFieldSet.Field.newBuilder() + .addVarint(2).build()) + .addField(3, + UnknownFieldSet.Field.newBuilder() + .addVarint(4).build()) + .build()) + .build(); + TestEmptyMessage destination = + TestEmptyMessage.newBuilder() + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(1, + UnknownFieldSet.Field.newBuilder() + .addVarint(1).build()) + .addField(3, + UnknownFieldSet.Field.newBuilder() + .addVarint(3).build()) + .build()) + .mergeFrom(source) + .build(); + + assertEquals( + "1: 1\n" + + "2: 2\n" + + "3: 3\n" + + "3: 4\n", + destination.toString()); + } + + public void testClear() throws Exception { + UnknownFieldSet fields = + UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build(); + assertTrue(fields.asMap().isEmpty()); + } + + public void testClearMessage() throws Exception { + TestEmptyMessage message = + TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).clear().build(); + assertEquals(0, message.getSerializedSize()); + } + + public void testParseKnownAndUnknown() throws Exception { + // Test mixing known and unknown fields when parsing. + + UnknownFieldSet fields = + UnknownFieldSet.newBuilder(unknownFields) + .addField(123456, + UnknownFieldSet.Field.newBuilder().addVarint(654321).build()) + .build(); + + ByteString data = fields.toByteString(); + TestAllTypes destination = TestAllTypes.parseFrom(data); + + TestUtil.assertAllFieldsSet(destination); + assertEquals(1, destination.getUnknownFields().asMap().size()); + + UnknownFieldSet.Field field = + destination.getUnknownFields().getField(123456); + assertEquals(1, field.getVarintList().size()); + assertEquals(654321, (long) field.getVarintList().get(0)); + } + + public void testWrongTypeTreatedAsUnknown() throws Exception { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing. + + ByteString bizarroData = getBizarroData(); + TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData); + TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + assertEquals(emptyMessage.toString(), allTypesMessage.toString()); + } + + public void testUnknownExtensions() throws Exception { + // Make sure fields are properly parsed to the UnknownFieldSet even when + // they are declared as extension numbers. + + TestEmptyMessageWithExtensions message = + TestEmptyMessageWithExtensions.parseFrom(allFieldsData); + + assertEquals(unknownFields.asMap().size(), + message.getUnknownFields().asMap().size()); + assertEquals(allFieldsData, message.toByteString()); + } + + public void testWrongExtensionTypeTreatedAsUnknown() throws Exception { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing extensions. + + ByteString bizarroData = getBizarroData(); + TestAllExtensions allExtensionsMessage = + TestAllExtensions.parseFrom(bizarroData); + TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + assertEquals(emptyMessage.toString(), + allExtensionsMessage.toString()); + } + + public void testParseUnknownEnumValue() throws Exception { + Descriptors.FieldDescriptor singularField = + TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum"); + Descriptors.FieldDescriptor repeatedField = + TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum"); + assertNotNull(singularField); + assertNotNull(repeatedField); + + ByteString data = + UnknownFieldSet.newBuilder() + .addField(singularField.getNumber(), + UnknownFieldSet.Field.newBuilder() + .addVarint(TestAllTypes.NestedEnum.BAR.getNumber()) + .addVarint(5) // not valid + .build()) + .addField(repeatedField.getNumber(), + UnknownFieldSet.Field.newBuilder() + .addVarint(TestAllTypes.NestedEnum.FOO.getNumber()) + .addVarint(4) // not valid + .addVarint(TestAllTypes.NestedEnum.BAZ.getNumber()) + .addVarint(6) // not valid + .build()) + .build() + .toByteString(); + + { + TestAllTypes message = TestAllTypes.parseFrom(data); + assertEquals(TestAllTypes.NestedEnum.BAR, + message.getOptionalNestedEnum()); + assertEquals( + Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ), + message.getRepeatedNestedEnumList()); + assertEquals(Arrays.asList(5L), + message.getUnknownFields() + .getField(singularField.getNumber()) + .getVarintList()); + assertEquals(Arrays.asList(4L, 6L), + message.getUnknownFields() + .getField(repeatedField.getNumber()) + .getVarintList()); + } + + { + TestAllExtensions message = + TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry()); + assertEquals(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.optionalNestedEnumExtension)); + assertEquals( + Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ), + message.getExtension(UnittestProto.repeatedNestedEnumExtension)); + assertEquals(Arrays.asList(5L), + message.getUnknownFields() + .getField(singularField.getNumber()) + .getVarintList()); + assertEquals(Arrays.asList(4L, 6L), + message.getUnknownFields() + .getField(repeatedField.getNumber()) + .getVarintList()); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/WireFormatTest.java b/java/src/test/java/com/google/protobuf/WireFormatTest.java new file mode 100644 index 00000000..84cc89f8 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/WireFormatTest.java @@ -0,0 +1,226 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import junit.framework.TestCase; +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestFieldOrderings; +import protobuf_unittest.UnittestMset.TestMessageSet; +import protobuf_unittest.UnittestMset.RawMessageSet; +import protobuf_unittest.UnittestMset.TestMessageSetExtension1; +import protobuf_unittest.UnittestMset.TestMessageSetExtension2; + +/** + * Tests related to parsing and serialization. + * + * @author kenton@google.com (Kenton Varda) + */ +public class WireFormatTest extends TestCase { + public void testSerialization() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + ByteString rawBytes = message.toByteString(); + assertEquals(rawBytes.size(), message.getSerializedSize()); + + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + + TestUtil.assertAllFieldsSet(message2); + } + + public void testSerializeExtensions() throws Exception { + // TestAllTypes and TestAllExtensions should have compatible wire formats, + // so if we serealize a TestAllExtensions then parse it as TestAllTypes + // it should work. + + TestAllExtensions message = TestUtil.getAllExtensionsSet(); + ByteString rawBytes = message.toByteString(); + assertEquals(rawBytes.size(), message.getSerializedSize()); + + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + + TestUtil.assertAllFieldsSet(message2); + } + + public void testParseExtensions() throws Exception { + // TestAllTypes and TestAllExtensions should have compatible wire formats, + // so if we serealize a TestAllTypes then parse it as TestAllExtensions + // it should work. + + TestAllTypes message = TestUtil.getAllSet(); + ByteString rawBytes = message.toByteString(); + + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + TestUtil.registerAllExtensions(registry); + registry = registry.getUnmodifiable(); + + TestAllExtensions message2 = + TestAllExtensions.parseFrom(rawBytes, registry); + + TestUtil.assertAllExtensionsSet(message2); + } + + public void testExtensionsSerializedSize() throws Exception { + assertEquals(TestUtil.getAllSet().getSerializedSize(), + TestUtil.getAllExtensionsSet().getSerializedSize()); + } + + private void assertFieldsInOrder(ByteString data) throws Exception { + CodedInputStream input = data.newCodedInput(); + int previousTag = 0; + + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + assertTrue(tag > previousTag); + input.skipField(tag); + } + } + + public void testInterleavedFieldsAndExtensions() throws Exception { + // Tests that fields are written in order even when extension ranges + // are interleaved with field numbers. + ByteString data = + TestFieldOrderings.newBuilder() + .setMyInt(1) + .setMyString("foo") + .setMyFloat(1.0F) + .setExtension(UnittestProto.myExtensionInt, 23) + .setExtension(UnittestProto.myExtensionString, "bar") + .build().toByteString(); + assertFieldsInOrder(data); + + Descriptors.Descriptor descriptor = TestFieldOrderings.getDescriptor(); + ByteString dynamic_data = + DynamicMessage.newBuilder(TestFieldOrderings.getDescriptor()) + .setField(descriptor.findFieldByName("my_int"), 1L) + .setField(descriptor.findFieldByName("my_string"), "foo") + .setField(descriptor.findFieldByName("my_float"), 1.0F) + .setField(UnittestProto.myExtensionInt.getDescriptor(), 23) + .setField(UnittestProto.myExtensionString.getDescriptor(), "bar") + .build().toByteString(); + assertFieldsInOrder(dynamic_data); + } + + private static final int UNKNOWN_TYPE_ID = 1550055; + private static final int TYPE_ID_1 = + TestMessageSetExtension1.getDescriptor().getExtensions().get(0).getNumber(); + private static final int TYPE_ID_2 = + TestMessageSetExtension2.getDescriptor().getExtensions().get(0).getNumber(); + + public void testSerializeMessageSet() throws Exception { + // Set up a TestMessageSet with two known messages and an unknown one. + TestMessageSet messageSet = + TestMessageSet.newBuilder() + .setExtension( + TestMessageSetExtension1.messageSetExtension, + TestMessageSetExtension1.newBuilder().setI(123).build()) + .setExtension( + TestMessageSetExtension2.messageSetExtension, + TestMessageSetExtension2.newBuilder().setStr("foo").build()) + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(UNKNOWN_TYPE_ID, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(ByteString.copyFromUtf8("bar")) + .build()) + .build()) + .build(); + + ByteString data = messageSet.toByteString(); + + // Parse back using RawMessageSet and check the contents. + RawMessageSet raw = RawMessageSet.parseFrom(data); + + assertTrue(raw.getUnknownFields().asMap().isEmpty()); + + assertEquals(3, raw.getItemCount()); + assertEquals(TYPE_ID_1, raw.getItem(0).getTypeId()); + assertEquals(TYPE_ID_2, raw.getItem(1).getTypeId()); + assertEquals(UNKNOWN_TYPE_ID, raw.getItem(2).getTypeId()); + + TestMessageSetExtension1 message1 = + TestMessageSetExtension1.parseFrom( + raw.getItem(0).getMessage().toByteArray()); + assertEquals(123, message1.getI()); + + TestMessageSetExtension2 message2 = + TestMessageSetExtension2.parseFrom( + raw.getItem(1).getMessage().toByteArray()); + assertEquals("foo", message2.getStr()); + + assertEquals("bar", raw.getItem(2).getMessage().toStringUtf8()); + } + + public void testParseMessageSet() throws Exception { + ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); + extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); + extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); + + // Set up a RawMessageSet with two known messages and an unknown one. + RawMessageSet raw = + RawMessageSet.newBuilder() + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_1) + .setMessage( + TestMessageSetExtension1.newBuilder() + .setI(123) + .build().toByteString()) + .build()) + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_2) + .setMessage( + TestMessageSetExtension2.newBuilder() + .setStr("foo") + .build().toByteString()) + .build()) + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(UNKNOWN_TYPE_ID) + .setMessage(ByteString.copyFromUtf8("bar")) + .build()) + .build(); + + ByteString data = raw.toByteString(); + + // Parse as a TestMessageSet and check the contents. + TestMessageSet messageSet = + TestMessageSet.parseFrom(data, extensionRegistry); + + assertEquals(123, messageSet.getExtension( + TestMessageSetExtension1.messageSetExtension).getI()); + assertEquals("foo", messageSet.getExtension( + TestMessageSetExtension2.messageSetExtension).getStr()); + + // Check for unknown field with type LENGTH_DELIMITED, + // number UNKNOWN_TYPE_ID, and contents "bar". + UnknownFieldSet unknownFields = messageSet.getUnknownFields(); + assertEquals(1, unknownFields.asMap().size()); + assertTrue(unknownFields.hasField(UNKNOWN_TYPE_ID)); + + UnknownFieldSet.Field field = unknownFields.getField(UNKNOWN_TYPE_ID); + assertEquals(1, field.getLengthDelimitedList().size()); + assertEquals("bar", field.getLengthDelimitedList().get(0).toStringUtf8()); + } +} + diff --git a/java/src/test/java/com/google/protobuf/multiple_files_test.proto b/java/src/test/java/com/google/protobuf/multiple_files_test.proto new file mode 100644 index 00000000..1dbadfe0 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/multiple_files_test.proto @@ -0,0 +1,53 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// +// A proto file which tests the java_multiple_files option. + + +import "google/protobuf/unittest.proto"; + +package protobuf_unittest; + +option java_multiple_files = true; +option java_outer_classname = "MultipleFilesTestProto"; + +message MessageWithNoOuter { + message NestedMessage { + optional int32 i = 1; + } + enum NestedEnum { + BAZ = 3; + } + optional NestedMessage nested = 1; + repeated TestAllTypes foreign = 2; + optional NestedEnum nested_enum = 3; + optional EnumWithNoOuter foreign_enum = 4; +} + +enum EnumWithNoOuter { + FOO = 1; + BAR = 2; +} + +service ServiceWithNoOuter { + rpc Foo(MessageWithNoOuter) returns(TestAllTypes); +} + +extend TestAllExtensions { + optional int32 extension_with_outer = 1234567; +} diff --git a/m4/acx_pthread.m4 b/m4/acx_pthread.m4 new file mode 100644 index 00000000..2cf20de1 --- /dev/null +++ b/m4/acx_pthread.m4 @@ -0,0 +1,363 @@ +# This was retrieved from +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?revision=1277&root=avahi +# See also (perhaps for new versions?) +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?root=avahi +# +# We've rewritten the inconsistency check code (from avahi), to work +# more broadly. In particular, it no longer assumes ld accepts -zdefs. +# This caused a restructing of the code, but the functionality has only +# changed a little. + +dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl +dnl @summary figure out how to build C programs using POSIX threads +dnl +dnl This macro figures out how to build C programs using POSIX threads. +dnl It sets the PTHREAD_LIBS output variable to the threads library and +dnl linker flags, and the PTHREAD_CFLAGS output variable to any special +dnl C compiler flags that are needed. (The user can also force certain +dnl compiler flags/libs to be tested by setting these environment +dnl variables.) +dnl +dnl Also sets PTHREAD_CC to any special C compiler that is needed for +dnl multi-threaded programs (defaults to the value of CC otherwise). +dnl (This is necessary on AIX to use the special cc_r compiler alias.) +dnl +dnl NOTE: You are assumed to not only compile your program with these +dnl flags, but also link it with them as well. e.g. you should link +dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS +dnl $LIBS +dnl +dnl If you are only building threads programs, you may wish to use +dnl these variables in your default LIBS, CFLAGS, and CC: +dnl +dnl LIBS="$PTHREAD_LIBS $LIBS" +dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +dnl CC="$PTHREAD_CC" +dnl +dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute +dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to +dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +dnl +dnl ACTION-IF-FOUND is a list of shell commands to run if a threads +dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to +dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the +dnl default action will define HAVE_PTHREAD. +dnl +dnl Please let the authors know if this macro fails on any platform, or +dnl if you have any other suggestions or comments. This macro was based +dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with +dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros +dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. +dnl We are also grateful for the helpful feedback of numerous users. +dnl +dnl @category InstalledPackages +dnl @author Steven G. Johnson +dnl @version 2006-05-29 +dnl @license GPLWithACException +dnl +dnl Checks for GCC shared/pthread inconsistency based on work by +dnl Marcin Owsiany + + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) + else + PTHREAD_CC=$CC + fi + + # The next part tries to detect GCC inconsistency with -shared on some + # architectures and systems. The problem is that in certain + # configurations, when -shared is specified, GCC "forgets" to + # internally use various flags which are still necessary. + + # + # Prepare the flags + # + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_CC="$CC" + + # Try with the flags determined by the earlier checks. + # + # -Wl,-z,defs forces link-time symbol resolution, so that the + # linking checks with -shared actually have any value + # + # FIXME: -fPIC is required for -shared on many architectures, + # so we specify it here, but the right way would probably be to + # properly detect whether it is actually required. + CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CC="$PTHREAD_CC" + + # In order not to create several levels of indentation, we test + # the value of "$done" until we find the cure or run out of ideas. + done="no" + + # First, make sure the CFLAGS we added are actually accepted by our + # compiler. If not (and OS X's ld, for instance, does not accept -z), + # then we can't do this test. + if test x"$done" = xno; then + AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) + AC_TRY_LINK(,, , [done=yes]) + + if test "x$done" = xyes ; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + fi + fi + + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + fi + + # + # Linux gcc on some architectures such as mips/mipsel forgets + # about -lpthread + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lpthread fixes that]) + LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + # + # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lc_r fixes that]) + LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + if test x"$done" = xno; then + # OK, we have run out of ideas + AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) + + # so it's not safe to assume that we may use pthreads + acx_pthread_ok=no + fi + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + CC="$save_CC" +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/m4/stl_hash.m4 b/m4/stl_hash.m4 new file mode 100644 index 00000000..912b954e --- /dev/null +++ b/m4/stl_hash.m4 @@ -0,0 +1,43 @@ +# We check two things: where the include file is for hash_map, and +# what namespace hash_map lives in within that include file. We +# include AC_TRY_COMPILE for all the combinations we've seen in the +# wild. We define one of HAVE_HASH_MAP or HAVE_EXT_HASH_MAP depending +# on location, and HASH_NAMESPACE to be the namespace hash_map is +# defined in. +# +# Ideally we'd use AC_CACHE_CHECK, but that only lets us store one value +# at a time, and we need to store two (filename and namespace). +# prints messages itself, so we have to do the message-printing ourselves +# via AC_MSG_CHECKING + AC_MSG_RESULT. (TODO(csilvers): can we cache?) + +AC_DEFUN([AC_CXX_STL_HASH], + [AC_MSG_CHECKING(the location of hash_map) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_cv_cxx_hash_map="" + for location in ext/hash_map hash_map; do + for namespace in __gnu_cxx "" std stdext; do + if test -z "$ac_cv_cxx_hash_map"; then + AC_TRY_COMPILE([#include <$location>], + [${namespace}::hash_map t], + [ac_cv_cxx_hash_map="<$location>"; + ac_cv_cxx_hash_namespace="$namespace";]) + fi + done + done + ac_cv_cxx_hash_set=`echo "$ac_cv_cxx_hash_map" | sed s/map/set/`; + if test -n "$ac_cv_cxx_hash_map"; then + AC_DEFINE(HAVE_HASH_MAP, 1, [define if the compiler has hash_map]) + AC_DEFINE(HAVE_HASH_SET, 1, [define if the compiler has hash_set]) + AC_DEFINE_UNQUOTED(HASH_MAP_H,$ac_cv_cxx_hash_map, + [the location of ]) + AC_DEFINE_UNQUOTED(HASH_SET_H,$ac_cv_cxx_hash_set, + [the location of ]) + AC_DEFINE_UNQUOTED(HASH_NAMESPACE,$ac_cv_cxx_hash_namespace, + [the namespace of hash_map/hash_set]) + AC_MSG_RESULT([$ac_cv_cxx_hash_map]) + else + AC_MSG_RESULT() + AC_MSG_WARN([could not find an STL hash_map]) + fi +]) diff --git a/python/README.txt b/python/README.txt new file mode 100644 index 00000000..44bbee0b --- /dev/null +++ b/python/README.txt @@ -0,0 +1,53 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. + +This directory contains the Python Protocol Buffers runtime library. + +Installation +============ + +1) Make sure you have Python 2.4 or newer. If in doubt, run: + + $ python -V + +2) If you do not have setuptools installed, note that it will be + downloaded and installed automatically as soon as you run setup.py. + If you would rather install it manually, you may do so by following + the instructions on this page: + + http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions + +3) Build the C++ code, or install a binary distribution of protoc. If + you install a binary distribution, make sure that it is the same + version as this package. If in doubt, run: + + $ protoc --version + +4) Run the tests: + + $ python setup.py test + + If some tests fail, this library may not work correctly on your + system. Continue at your own risk. + + Please note that there is a known problem with some versions of + Python on Cygwin which causes the tests to fail after printing the + error: "sem_init: Resource temporarily unavailable". This appears + to be a bug either in Cygwin or in Python: + http://www.cygwin.com/ml/cygwin/2005-07/msg01378.html + We do not know if or when it might me fixed. We also do not know + how likely it is that this bug will affect users in practice. + +5) Install: + + $ python setup.py install + + This step may require superuser privileges. + +Usage +===== + +The complete documentation for Protocol Buffers is available via the +web at: + + http://code.google.com/apis/protocolbuffers/ diff --git a/python/ez_setup.py b/python/ez_setup.py new file mode 100755 index 00000000..989d366c --- /dev/null +++ b/python/ez_setup.py @@ -0,0 +1,277 @@ +#!python + +# This file was obtained from: +# http://peak.telecommunity.com/dist/ez_setup.py +# on 2008/7/1. + +"""Bootstrap setuptools installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import sys +DEFAULT_VERSION = "0.6c8" +DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] + +md5_data = { + 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', + 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', + 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', + 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', + 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', + 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', + 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', + 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', + 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', + 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', + 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', + 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', + 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', + 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', + 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', + 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', + 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', + 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', + 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', + 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', + 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', + 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', + 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', + 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', + 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', + 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', + 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', + 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', + 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', + 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', +} + +import sys, os + +def _validate_md5(egg_name, data): + if egg_name in md5_data: + from md5 import md5 + digest = md5(data).hexdigest() + if digest != md5_data[egg_name]: + print >>sys.stderr, ( + "md5 validation of %s failed! (Possible download problem?)" + % egg_name + ) + sys.exit(2) + return data + + +def use_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + download_delay=15 +): + """Automatically find/download setuptools and make it available on sys.path + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end with + a '/'). `to_dir` is the directory where setuptools will be downloaded, if + it is not already available. If `download_delay` is specified, it should + be the number of seconds that will be paused before initiating a download, + should one be required. If an older version of setuptools is installed, + this routine will print a message to ``sys.stderr`` and raise SystemExit in + an attempt to abort the calling script. + """ + was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules + def do_download(): + egg = download_setuptools(version, download_base, to_dir, download_delay) + sys.path.insert(0, egg) + import setuptools; setuptools.bootstrap_install_from = egg + try: + import pkg_resources + except ImportError: + return do_download() + try: + pkg_resources.require("setuptools>="+version); return + except pkg_resources.VersionConflict, e: + if was_imported: + print >>sys.stderr, ( + "The required version of setuptools (>=%s) is not available, and\n" + "can't be installed while this script is running. Please install\n" + " a more recent version first, using 'easy_install -U setuptools'." + "\n\n(Currently using %r)" + ) % (version, e.args[0]) + sys.exit(2) + else: + del pkg_resources, sys.modules['pkg_resources'] # reload ok + return do_download() + except pkg_resources.DistributionNotFound: + return do_download() + +def download_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + delay = 15 +): + """Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download attempt. + """ + import urllib2, shutil + egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) + url = download_base + egg_name + saveto = os.path.join(to_dir, egg_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + from distutils import log + if delay: + log.warn(""" +--------------------------------------------------------------------------- +This script requires setuptools version %s to run (even to display +help). I will attempt to download it for you (from +%s), but +you may need to enable firewall access for this script first. +I will start the download in %d seconds. + +(Note: if this machine does not have network access, please obtain the file + + %s + +and place it in this directory before rerunning this script.) +---------------------------------------------------------------------------""", + version, download_base, delay, url + ); from time import sleep; sleep(delay) + log.warn("Downloading %s", url) + src = urllib2.urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = _validate_md5(egg_name, src.read()) + dst = open(saveto,"wb"); dst.write(data) + finally: + if src: src.close() + if dst: dst.close() + return os.path.realpath(saveto) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + try: + import setuptools + except ImportError: + egg = None + try: + egg = download_setuptools(version, delay=0) + sys.path.insert(0,egg) + from setuptools.command.easy_install import main + return main(list(argv)+[egg]) # we're done here + finally: + if egg and os.path.exists(egg): + os.unlink(egg) + else: + if setuptools.__version__ == '0.0.1': + print >>sys.stderr, ( + "You have an obsolete version of setuptools installed. Please\n" + "remove it from your system entirely before rerunning this script." + ) + sys.exit(2) + + req = "setuptools>="+version + import pkg_resources + try: + pkg_resources.require(req) + except pkg_resources.VersionConflict: + try: + from setuptools.command.easy_install import main + except ImportError: + from easy_install import main + main(list(argv)+[download_setuptools(delay=0)]) + sys.exit(0) # try to force an exit + else: + if argv: + from setuptools.command.easy_install import main + main(argv) + else: + print "Setuptools version",version,"or greater has been installed." + print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' + +def update_md5(filenames): + """Update our built-in md5 registry""" + + import re + from md5 import md5 + + for name in filenames: + base = os.path.basename(name) + f = open(name,'rb') + md5_data[base] = md5(f.read()).hexdigest() + f.close() + + data = [" %r: %r,\n" % it for it in md5_data.items()] + data.sort() + repl = "".join(data) + + import inspect + srcfile = inspect.getsourcefile(sys.modules[__name__]) + f = open(srcfile, 'rb'); src = f.read(); f.close() + + match = re.search("\nmd5_data = {\n([^}]+)}", src) + if not match: + print >>sys.stderr, "Internal error!" + sys.exit(2) + + src = src[:match.start(1)] + repl + src[match.end(1):] + f = open(srcfile,'w') + f.write(src) + f.close() + + +if __name__=='__main__': + if len(sys.argv)>2 and sys.argv[1]=='--md5update': + update_md5(sys.argv[2:]) + else: + main(sys.argv[1:]) + + + + + diff --git a/python/google/__init__.py b/python/google/__init__.py new file mode 100755 index 00000000..de40ea7c --- /dev/null +++ b/python/google/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py new file mode 100755 index 00000000..04748053 --- /dev/null +++ b/python/google/protobuf/descriptor.py @@ -0,0 +1,419 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO(robinson): We probably need to provide deep-copy methods for +# descriptor types. When a FieldDescriptor is passed into +# Descriptor.__init__(), we should make a deep copy and then set +# containing_type on it. Alternatively, we could just get +# rid of containing_type (iit's not needed for reflection.py, at least). +# +# TODO(robinson): Print method? +# +# TODO(robinson): Useful __repr__? + +"""Descriptors essentially contain exactly the information found in a .proto +file, in types that make this information accessible in Python. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +class DescriptorBase(object): + + """Descriptors base class. + + This class is the base of all descriptor classes. It provides common options + related functionaility. + """ + + def __init__(self, options, options_class_name): + """Initialize the descriptor given its options message and the name of the + class of the options message. The name of the class is required in case + the options message is None and has to be created. + """ + self._options = options + self._options_class_name = options_class_name + + def GetOptions(self): + """Retrieves descriptor options. + + This method returns the options set or creates the default options for the + descriptor. + """ + if self._options: + return self._options + from google.protobuf import descriptor_pb2 + try: + options_class = getattr(descriptor_pb2, self._options_class_name) + except AttributeError: + raise RuntimeError('Unknown options class name %s!' % + (self._options_class_name)) + self._options = options_class() + return self._options + + +class Descriptor(DescriptorBase): + + """Descriptor for a protocol message type. + + A Descriptor instance has the following attributes: + + name: (str) Name of this protocol message type. + full_name: (str) Fully-qualified name of this protocol message type, + which will include protocol "package" name and the name of any + enclosing types. + + filename: (str) Name of the .proto file containing this message. + + containing_type: (Descriptor) Reference to the descriptor of the + type containing us, or None if we have no containing type. + + fields: (list of FieldDescriptors) Field descriptors for all + fields in this type. + fields_by_number: (dict int -> FieldDescriptor) Same FieldDescriptor + objects as in |fields|, but indexed by "number" attribute in each + FieldDescriptor. + fields_by_name: (dict str -> FieldDescriptor) Same FieldDescriptor + objects as in |fields|, but indexed by "name" attribute in each + FieldDescriptor. + + nested_types: (list of Descriptors) Descriptor references + for all protocol message types nested within this one. + nested_types_by_name: (dict str -> Descriptor) Same Descriptor + objects as in |nested_types|, but indexed by "name" attribute + in each Descriptor. + + enum_types: (list of EnumDescriptors) EnumDescriptor references + for all enums contained within this type. + enum_types_by_name: (dict str ->EnumDescriptor) Same EnumDescriptor + objects as in |enum_types|, but indexed by "name" attribute + in each EnumDescriptor. + enum_values_by_name: (dict str -> EnumValueDescriptor) Dict mapping + from enum value name to EnumValueDescriptor for that value. + + extensions: (list of FieldDescriptor) All extensions defined directly + within this message type (NOT within a nested type). + extensions_by_name: (dict, string -> FieldDescriptor) Same FieldDescriptor + objects as |extensions|, but indexed by "name" attribute of each + FieldDescriptor. + + options: (descriptor_pb2.MessageOptions) Protocol message options or None + to use default message options. + """ + + def __init__(self, name, full_name, filename, containing_type, + fields, nested_types, enum_types, extensions, options=None): + """Arguments to __init__() are as described in the description + of Descriptor fields above. + """ + super(Descriptor, self).__init__(options, 'MessageOptions') + self.name = name + self.full_name = full_name + self.filename = filename + self.containing_type = containing_type + + # We have fields in addition to fields_by_name and fields_by_number, + # so that: + # 1. Clients can index fields by "order in which they're listed." + # 2. Clients can easily iterate over all fields with the terse + # syntax: for f in descriptor.fields: ... + self.fields = fields + for field in self.fields: + field.containing_type = self + self.fields_by_number = dict((f.number, f) for f in fields) + self.fields_by_name = dict((f.name, f) for f in fields) + + self.nested_types = nested_types + self.nested_types_by_name = dict((t.name, t) for t in nested_types) + + self.enum_types = enum_types + for enum_type in self.enum_types: + enum_type.containing_type = self + self.enum_types_by_name = dict((t.name, t) for t in enum_types) + self.enum_values_by_name = dict( + (v.name, v) for t in enum_types for v in t.values) + + self.extensions = extensions + for extension in self.extensions: + extension.extension_scope = self + self.extensions_by_name = dict((f.name, f) for f in extensions) + + +# TODO(robinson): We should have aggressive checking here, +# for example: +# * If you specify a repeated field, you should not be allowed +# to specify a default value. +# * [Other examples here as needed]. +# +# TODO(robinson): for this and other *Descriptor classes, we +# might also want to lock things down aggressively (e.g., +# prevent clients from setting the attributes). Having +# stronger invariants here in general will reduce the number +# of runtime checks we must do in reflection.py... +class FieldDescriptor(DescriptorBase): + + """Descriptor for a single field in a .proto file. + + A FieldDescriptor instance has the following attriubtes: + + name: (str) Name of this field, exactly as it appears in .proto. + full_name: (str) Name of this field, including containing scope. This is + particularly relevant for extensions. + index: (int) Dense, 0-indexed index giving the order that this + field textually appears within its message in the .proto file. + number: (int) Tag number declared for this field in the .proto file. + + type: (One of the TYPE_* constants below) Declared type. + cpp_type: (One of the CPPTYPE_* constants below) C++ type used to + represent this field. + + label: (One of the LABEL_* constants below) Tells whether this + field is optional, required, or repeated. + default_value: (Varies) Default value of this field. Only + meaningful for non-repeated scalar fields. Repeated fields + should always set this to [], and non-repeated composite + fields should always set this to None. + + containing_type: (Descriptor) Descriptor of the protocol message + type that contains this field. Set by the Descriptor constructor + if we're passed into one. + Somewhat confusingly, for extension fields, this is the + descriptor of the EXTENDED message, not the descriptor + of the message containing this field. (See is_extension and + extension_scope below). + message_type: (Descriptor) If a composite field, a descriptor + of the message type contained in this field. Otherwise, this is None. + enum_type: (EnumDescriptor) If this field contains an enum, a + descriptor of that enum. Otherwise, this is None. + + is_extension: True iff this describes an extension field. + extension_scope: (Descriptor) Only meaningful if is_extension is True. + Gives the message that immediately contains this extension field. + Will be None iff we're a top-level (file-level) extension field. + + options: (descriptor_pb2.FieldOptions) Protocol message field options or + None to use default field options. + """ + + # Must be consistent with C++ FieldDescriptor::Type enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + TYPE_DOUBLE = 1 + TYPE_FLOAT = 2 + TYPE_INT64 = 3 + TYPE_UINT64 = 4 + TYPE_INT32 = 5 + TYPE_FIXED64 = 6 + TYPE_FIXED32 = 7 + TYPE_BOOL = 8 + TYPE_STRING = 9 + TYPE_GROUP = 10 + TYPE_MESSAGE = 11 + TYPE_BYTES = 12 + TYPE_UINT32 = 13 + TYPE_ENUM = 14 + TYPE_SFIXED32 = 15 + TYPE_SFIXED64 = 16 + TYPE_SINT32 = 17 + TYPE_SINT64 = 18 + MAX_TYPE = 18 + + # Must be consistent with C++ FieldDescriptor::CppType enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + CPPTYPE_INT32 = 1 + CPPTYPE_INT64 = 2 + CPPTYPE_UINT32 = 3 + CPPTYPE_UINT64 = 4 + CPPTYPE_DOUBLE = 5 + CPPTYPE_FLOAT = 6 + CPPTYPE_BOOL = 7 + CPPTYPE_ENUM = 8 + CPPTYPE_STRING = 9 + CPPTYPE_MESSAGE = 10 + MAX_CPPTYPE = 10 + + # Must be consistent with C++ FieldDescriptor::Label enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + LABEL_OPTIONAL = 1 + LABEL_REQUIRED = 2 + LABEL_REPEATED = 3 + MAX_LABEL = 3 + + def __init__(self, name, full_name, index, number, type, cpp_type, label, + default_value, message_type, enum_type, containing_type, + is_extension, extension_scope, options=None): + """The arguments are as described in the description of FieldDescriptor + attributes above. + + Note that containing_type may be None, and may be set later if necessary + (to deal with circular references between message types, for example). + Likewise for extension_scope. + """ + super(FieldDescriptor, self).__init__(options, 'FieldOptions') + self.name = name + self.full_name = full_name + self.index = index + self.number = number + self.type = type + self.cpp_type = cpp_type + self.label = label + self.default_value = default_value + self.containing_type = containing_type + self.message_type = message_type + self.enum_type = enum_type + self.is_extension = is_extension + self.extension_scope = extension_scope + + +class EnumDescriptor(DescriptorBase): + + """Descriptor for an enum defined in a .proto file. + + An EnumDescriptor instance has the following attributes: + + name: (str) Name of the enum type. + full_name: (str) Full name of the type, including package name + and any enclosing type(s). + filename: (str) Name of the .proto file in which this appears. + + values: (list of EnumValueDescriptors) List of the values + in this enum. + values_by_name: (dict str -> EnumValueDescriptor) Same as |values|, + but indexed by the "name" field of each EnumValueDescriptor. + values_by_number: (dict int -> EnumValueDescriptor) Same as |values|, + but indexed by the "number" field of each EnumValueDescriptor. + containing_type: (Descriptor) Descriptor of the immediate containing + type of this enum, or None if this is an enum defined at the + top level in a .proto file. Set by Descriptor's constructor + if we're passed into one. + options: (descriptor_pb2.EnumOptions) Enum options message or + None to use default enum options. + """ + + def __init__(self, name, full_name, filename, values, + containing_type=None, options=None): + """Arguments are as described in the attribute description above.""" + super(EnumDescriptor, self).__init__(options, 'EnumOptions') + self.name = name + self.full_name = full_name + self.filename = filename + self.values = values + for value in self.values: + value.type = self + self.values_by_name = dict((v.name, v) for v in values) + self.values_by_number = dict((v.number, v) for v in values) + self.containing_type = containing_type + + +class EnumValueDescriptor(DescriptorBase): + + """Descriptor for a single value within an enum. + + name: (str) Name of this value. + index: (int) Dense, 0-indexed index giving the order that this + value appears textually within its enum in the .proto file. + number: (int) Actual number assigned to this enum value. + type: (EnumDescriptor) EnumDescriptor to which this value + belongs. Set by EnumDescriptor's constructor if we're + passed into one. + options: (descriptor_pb2.EnumValueOptions) Enum value options message or + None to use default enum value options options. + """ + + def __init__(self, name, index, number, type=None, options=None): + """Arguments are as described in the attribute description above.""" + super(EnumValueDescriptor, self).__init__(options, 'EnumValueOptions') + self.name = name + self.index = index + self.number = number + self.type = type + + +class ServiceDescriptor(DescriptorBase): + + """Descriptor for a service. + + name: (str) Name of the service. + full_name: (str) Full name of the service, including package name. + index: (int) 0-indexed index giving the order that this services + definition appears withing the .proto file. + methods: (list of MethodDescriptor) List of methods provided by this + service. + options: (descriptor_pb2.ServiceOptions) Service options message or + None to use default service options. + """ + + def __init__(self, name, full_name, index, methods, options=None): + super(ServiceDescriptor, self).__init__(options, 'ServiceOptions') + self.name = name + self.full_name = full_name + self.index = index + self.methods = methods + # Set the containing service for each method in this service. + for method in self.methods: + method.containing_service = self + + def FindMethodByName(self, name): + """Searches for the specified method, and returns its descriptor.""" + for method in self.methods: + if name == method.name: + return method + return None + + +class MethodDescriptor(DescriptorBase): + + """Descriptor for a method in a service. + + name: (str) Name of the method within the service. + full_name: (str) Full name of method. + index: (int) 0-indexed index of the method inside the service. + containing_service: (ServiceDescriptor) The service that contains this + method. + input_type: The descriptor of the message that this method accepts. + output_type: The descriptor of the message that this method returns. + options: (descriptor_pb2.MethodOptions) Method options message or + None to use default method options. + """ + + def __init__(self, name, full_name, index, containing_service, + input_type, output_type, options=None): + """The arguments are as described in the description of MethodDescriptor + attributes above. + + Note that containing_service may be None, and may be set later if necessary. + """ + super(MethodDescriptor, self).__init__(options, 'MethodOptions') + self.name = name + self.full_name = full_name + self.index = index + self.containing_service = containing_service + self.input_type = input_type + self.output_type = output_type + + +def _ParseOptions(message, string): + """Parses serialized options. + + This helper function is used to parse serialized options in generated + proto2 files. It must not be used outside proto2. + """ + message.ParseFromString(string) + return message; diff --git a/python/google/protobuf/internal/__init__.py b/python/google/protobuf/internal/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py new file mode 100755 index 00000000..b81f04a5 --- /dev/null +++ b/python/google/protobuf/internal/decoder.py @@ -0,0 +1,194 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Class for decoding protocol buffer primitives. + +Contains the logic for decoding every logical protocol field type +from one of the 5 physical wire types. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message +from google.protobuf.internal import input_stream +from google.protobuf.internal import wire_format + + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by WireFormat from the C++ proto2 +# implementation. + + +class Decoder(object): + + """Decodes logical protocol buffer fields from the wire.""" + + def __init__(self, s): + """Initializes the decoder to read from s. + + Args: + s: An immutable sequence of bytes, which must be accessible + via the Python buffer() primitive (i.e., buffer(s)). + """ + self._stream = input_stream.InputStream(s) + + def EndOfStream(self): + """Returns true iff we've reached the end of the bytes we're reading.""" + return self._stream.EndOfStream() + + def Position(self): + """Returns the 0-indexed position in |s|.""" + return self._stream.Position() + + def ReadFieldNumberAndWireType(self): + """Reads a tag from the wire. Returns a (field_number, wire_type) pair.""" + tag_and_type = self.ReadUInt32() + return wire_format.UnpackTag(tag_and_type) + + def SkipBytes(self, bytes): + """Skips the specified number of bytes on the wire.""" + self._stream.SkipBytes(bytes) + + # Note that the Read*() methods below are not exactly symmetrical with the + # corresponding Encoder.Append*() methods. Those Encoder methods first + # encode a tag, but the Read*() methods below assume that the tag has already + # been read, and that the client wishes to read a field of the specified type + # starting at the current position. + + def ReadInt32(self): + """Reads and returns a signed, varint-encoded, 32-bit integer.""" + return self._stream.ReadVarint32() + + def ReadInt64(self): + """Reads and returns a signed, varint-encoded, 64-bit integer.""" + return self._stream.ReadVarint64() + + def ReadUInt32(self): + """Reads and returns an signed, varint-encoded, 32-bit integer.""" + return self._stream.ReadVarUInt32() + + def ReadUInt64(self): + """Reads and returns an signed, varint-encoded,64-bit integer.""" + return self._stream.ReadVarUInt64() + + def ReadSInt32(self): + """Reads and returns a signed, zigzag-encoded, varint-encoded, + 32-bit integer.""" + return wire_format.ZigZagDecode(self._stream.ReadVarUInt32()) + + def ReadSInt64(self): + """Reads and returns a signed, zigzag-encoded, varint-encoded, + 64-bit integer.""" + return wire_format.ZigZagDecode(self._stream.ReadVarUInt64()) + + def ReadFixed32(self): + """Reads and returns an unsigned, fixed-width, 32-bit integer.""" + return self._stream.ReadLittleEndian32() + + def ReadFixed64(self): + """Reads and returns an unsigned, fixed-width, 64-bit integer.""" + return self._stream.ReadLittleEndian64() + + def ReadSFixed32(self): + """Reads and returns a signed, fixed-width, 32-bit integer.""" + value = self._stream.ReadLittleEndian32() + if value >= (1 << 31): + value -= (1 << 32) + return value + + def ReadSFixed64(self): + """Reads and returns a signed, fixed-width, 64-bit integer.""" + value = self._stream.ReadLittleEndian64() + if value >= (1 << 63): + value -= (1 << 64) + return value + + def ReadFloat(self): + """Reads and returns a 4-byte floating-point number.""" + serialized = self._stream.ReadString(4) + return struct.unpack('f', serialized)[0] + + def ReadDouble(self): + """Reads and returns an 8-byte floating-point number.""" + serialized = self._stream.ReadString(8) + return struct.unpack('d', serialized)[0] + + def ReadBool(self): + """Reads and returns a bool.""" + i = self._stream.ReadVarUInt32() + return bool(i) + + def ReadEnum(self): + """Reads and returns an enum value.""" + return self._stream.ReadVarUInt32() + + def ReadString(self): + """Reads and returns a length-delimited string.""" + length = self._stream.ReadVarUInt32() + return self._stream.ReadString(length) + + def ReadBytes(self): + """Reads and returns a length-delimited byte sequence.""" + return self.ReadString() + + def ReadMessageInto(self, msg): + """Calls msg.MergeFromString() to merge + length-delimited serialized message data into |msg|. + + REQUIRES: The decoder must be positioned at the serialized "length" + prefix to a length-delmiited serialized message. + + POSTCONDITION: The decoder is positioned just after the + serialized message, and we have merged those serialized + contents into |msg|. + """ + length = self._stream.ReadVarUInt32() + sub_buffer = self._stream.GetSubBuffer(length) + num_bytes_used = msg.MergeFromString(sub_buffer) + if num_bytes_used != length: + raise message.DecodeError( + 'Submessage told to deserialize from %d-byte encoding, ' + 'but used only %d bytes' % (length, num_bytes_used)) + self._stream.SkipBytes(num_bytes_used) + + def ReadGroupInto(self, expected_field_number, group): + """Calls group.MergeFromString() to merge + END_GROUP-delimited serialized message data into |group|. + We'll raise an exception if we don't find an END_GROUP + tag immediately after the serialized message contents. + + REQUIRES: The decoder is positioned just after the START_GROUP + tag for this group. + + POSTCONDITION: The decoder is positioned just after the + END_GROUP tag for this group, and we have merged + the contents of the group into |group|. + """ + sub_buffer = self._stream.GetSubBuffer() # No a priori length limit. + num_bytes_used = group.MergeFromString(sub_buffer) + if num_bytes_used < 0: + raise message.DecodeError('Group message reported negative bytes read.') + self._stream.SkipBytes(num_bytes_used) + field_number, field_type = self.ReadFieldNumberAndWireType() + if field_type != wire_format.WIRETYPE_END_GROUP: + raise message.DecodeError('Group message did not end with an END_GROUP.') + if field_number != expected_field_number: + raise message.DecodeError('END_GROUP tag had field ' + 'number %d, was expecting field number %d' % ( + field_number, expected_field_number)) + # We're now positioned just after the END_GROUP tag. Perfect. diff --git a/python/google/protobuf/internal/decoder_test.py b/python/google/protobuf/internal/decoder_test.py new file mode 100755 index 00000000..e36a96fc --- /dev/null +++ b/python/google/protobuf/internal/decoder_test.py @@ -0,0 +1,230 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test for google.protobuf.internal.decoder.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +import unittest +from google.protobuf.internal import wire_format +from google.protobuf.internal import encoder +from google.protobuf.internal import decoder +import logging +import mox +from google.protobuf.internal import input_stream +from google.protobuf import message + + +class DecoderTest(unittest.TestCase): + + def setUp(self): + self.mox = mox.Mox() + self.mock_stream = self.mox.CreateMock(input_stream.InputStream) + self.mock_message = self.mox.CreateMock(message.Message) + + def testReadFieldNumberAndWireType(self): + # Test field numbers that will require various varint sizes. + for expected_field_number in (1, 15, 16, 2047, 2048): + for expected_wire_type in range(6): # Highest-numbered wiretype is 5. + e = encoder.Encoder() + e._AppendTag(expected_field_number, expected_wire_type) + s = e.ToString() + d = decoder.Decoder(s) + field_number, wire_type = d.ReadFieldNumberAndWireType() + self.assertEqual(expected_field_number, field_number) + self.assertEqual(expected_wire_type, wire_type) + + def ReadScalarTestHelper(self, test_name, decoder_method, expected_result, + expected_stream_method_name, + stream_method_return, *args): + """Helper for testReadScalars below. + + Calls one of the Decoder.Read*() methods and ensures that the results are + as expected. + + Args: + test_name: Name of this test, used for logging only. + decoder_method: Unbound decoder.Decoder method to call. + expected_result: Value we expect returned from decoder_method(). + expected_stream_method_name: (string) Name of the InputStream + method we expect Decoder to call to actually read the value + on the wire. + stream_method_return: Value our mocked-out stream method should + return to the decoder. + args: Additional arguments that we expect to be passed to the + stream method. + """ + logging.info('Testing %s scalar input.\n' + 'Calling %r(), and expecting that to call the ' + 'stream method %s(%r), which will return %r. Finally, ' + 'expecting the Decoder method to return %r'% ( + test_name, decoder_method, + expected_stream_method_name, args, stream_method_return, + expected_result)) + + d = decoder.Decoder('') + d._stream = self.mock_stream + if decoder_method in (decoder.Decoder.ReadString, + decoder.Decoder.ReadBytes): + self.mock_stream.ReadVarUInt32().AndReturn(len(stream_method_return)) + # We have to use names instead of methods to work around some + # mox weirdness. (ResetAll() is overzealous). + expected_stream_method = getattr(self.mock_stream, + expected_stream_method_name) + expected_stream_method(*args).AndReturn(stream_method_return) + + self.mox.ReplayAll() + self.assertEqual(expected_result, decoder_method(d)) + self.mox.VerifyAll() + self.mox.ResetAll() + + def testReadScalars(self): + test_string = 'I can feel myself getting sutpider.' + scalar_tests = [ + ['int32', decoder.Decoder.ReadInt32, 0, 'ReadVarint32', 0], + ['int64', decoder.Decoder.ReadInt64, 0, 'ReadVarint64', 0], + ['uint32', decoder.Decoder.ReadUInt32, 0, 'ReadVarUInt32', 0], + ['uint64', decoder.Decoder.ReadUInt64, 0, 'ReadVarUInt64', 0], + ['fixed32', decoder.Decoder.ReadFixed32, 0xffffffff, + 'ReadLittleEndian32', 0xffffffff], + ['fixed64', decoder.Decoder.ReadFixed64, 0xffffffffffffffff, + 'ReadLittleEndian64', 0xffffffffffffffff], + ['sfixed32', decoder.Decoder.ReadSFixed32, -1, + 'ReadLittleEndian32', 0xffffffff], + ['sfixed64', decoder.Decoder.ReadSFixed64, -1, + 'ReadLittleEndian64', 0xffffffffffffffff], + ['float', decoder.Decoder.ReadFloat, 0.0, + 'ReadString', struct.pack('f', 0.0), 4], + ['double', decoder.Decoder.ReadDouble, 0.0, + 'ReadString', struct.pack('d', 0.0), 8], + ['bool', decoder.Decoder.ReadBool, True, 'ReadVarUInt32', 1], + ['enum', decoder.Decoder.ReadEnum, 23, 'ReadVarUInt32', 23], + ['string', decoder.Decoder.ReadString, + test_string, 'ReadString', test_string, len(test_string)], + ['bytes', decoder.Decoder.ReadBytes, + test_string, 'ReadString', test_string, len(test_string)], + # We test zigzag decoding routines more extensively below. + ['sint32', decoder.Decoder.ReadSInt32, -1, 'ReadVarUInt32', 1], + ['sint64', decoder.Decoder.ReadSInt64, -1, 'ReadVarUInt64', 1], + ] + # Ensure that we're testing different Decoder methods and using + # different test names in all test cases above. + self.assertEqual(len(scalar_tests), len(set(t[0] for t in scalar_tests))) + self.assertEqual(len(scalar_tests), len(set(t[1] for t in scalar_tests))) + for args in scalar_tests: + self.ReadScalarTestHelper(*args) + + def testReadMessageInto(self): + length = 23 + def Test(simulate_error): + d = decoder.Decoder('') + d._stream = self.mock_stream + self.mock_stream.ReadVarUInt32().AndReturn(length) + sub_buffer = object() + self.mock_stream.GetSubBuffer(length).AndReturn(sub_buffer) + + if simulate_error: + self.mock_message.MergeFromString(sub_buffer).AndReturn(length - 1) + self.mox.ReplayAll() + self.assertRaises( + message.DecodeError, d.ReadMessageInto, self.mock_message) + else: + self.mock_message.MergeFromString(sub_buffer).AndReturn(length) + self.mock_stream.SkipBytes(length) + self.mox.ReplayAll() + d.ReadMessageInto(self.mock_message) + + self.mox.VerifyAll() + self.mox.ResetAll() + + Test(simulate_error=False) + Test(simulate_error=True) + + def testReadGroupInto_Success(self): + # Test both the empty and nonempty cases. + for num_bytes in (5, 0): + field_number = expected_field_number = 10 + d = decoder.Decoder('') + d._stream = self.mock_stream + sub_buffer = object() + self.mock_stream.GetSubBuffer().AndReturn(sub_buffer) + self.mock_message.MergeFromString(sub_buffer).AndReturn(num_bytes) + self.mock_stream.SkipBytes(num_bytes) + self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag( + field_number, wire_format.WIRETYPE_END_GROUP)) + self.mox.ReplayAll() + d.ReadGroupInto(expected_field_number, self.mock_message) + self.mox.VerifyAll() + self.mox.ResetAll() + + def ReadGroupInto_FailureTestHelper(self, bytes_read): + d = decoder.Decoder('') + d._stream = self.mock_stream + sub_buffer = object() + self.mock_stream.GetSubBuffer().AndReturn(sub_buffer) + self.mock_message.MergeFromString(sub_buffer).AndReturn(bytes_read) + return d + + def testReadGroupInto_NegativeBytesReported(self): + expected_field_number = 10 + d = self.ReadGroupInto_FailureTestHelper(bytes_read=-1) + self.mox.ReplayAll() + self.assertRaises(message.DecodeError, + d.ReadGroupInto, expected_field_number, + self.mock_message) + self.mox.VerifyAll() + + def testReadGroupInto_NoEndGroupTag(self): + field_number = expected_field_number = 10 + num_bytes = 5 + d = self.ReadGroupInto_FailureTestHelper(bytes_read=num_bytes) + self.mock_stream.SkipBytes(num_bytes) + # Right field number, wrong wire type. + self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag( + field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)) + self.mox.ReplayAll() + self.assertRaises(message.DecodeError, + d.ReadGroupInto, expected_field_number, + self.mock_message) + self.mox.VerifyAll() + + def testReadGroupInto_WrongFieldNumberInEndGroupTag(self): + expected_field_number = 10 + field_number = expected_field_number + 1 + num_bytes = 5 + d = self.ReadGroupInto_FailureTestHelper(bytes_read=num_bytes) + self.mock_stream.SkipBytes(num_bytes) + # Wrong field number, right wire type. + self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag( + field_number, wire_format.WIRETYPE_END_GROUP)) + self.mox.ReplayAll() + self.assertRaises(message.DecodeError, + d.ReadGroupInto, expected_field_number, + self.mock_message) + self.mox.VerifyAll() + + def testSkipBytes(self): + d = decoder.Decoder('') + num_bytes = 1024 + self.mock_stream.SkipBytes(num_bytes) + d._stream = self.mock_stream + self.mox.ReplayAll() + d.SkipBytes(num_bytes) + self.mox.VerifyAll() + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py new file mode 100755 index 00000000..625d0326 --- /dev/null +++ b/python/google/protobuf/internal/descriptor_test.py @@ -0,0 +1,97 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for google.protobuf.internal.descriptor.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor + +class DescriptorTest(unittest.TestCase): + + def setUp(self): + self.my_enum = descriptor.EnumDescriptor( + name='ForeignEnum', + full_name='protobuf_unittest.ForeignEnum', + filename='ForeignEnum', + values=[ + descriptor.EnumValueDescriptor(name='FOREIGN_FOO', index=0, number=4), + descriptor.EnumValueDescriptor(name='FOREIGN_BAR', index=1, number=5), + descriptor.EnumValueDescriptor(name='FOREIGN_BAZ', index=2, number=6), + ]) + self.my_message = descriptor.Descriptor( + name='NestedMessage', + full_name='protobuf_unittest.TestAllTypes.NestedMessage', + filename='some/filename/some.proto', + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='bb', + full_name='protobuf_unittest.TestAllTypes.NestedMessage.bb', + index=0, number=1, + type=5, cpp_type=1, label=1, + default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None), + ], + nested_types=[], + enum_types=[ + self.my_enum, + ], + extensions=[]) + self.my_method = descriptor.MethodDescriptor( + name='Bar', + full_name='protobuf_unittest.TestService.Bar', + index=0, + containing_service=None, + input_type=None, + output_type=None) + self.my_service = descriptor.ServiceDescriptor( + name='TestServiceWithOptions', + full_name='protobuf_unittest.TestServiceWithOptions', + index=0, + methods=[ + self.my_method + ]) + + def testEnumFixups(self): + self.assertEqual(self.my_enum, self.my_enum.values[0].type) + + def testContainingTypeFixups(self): + self.assertEqual(self.my_message, self.my_message.fields[0].containing_type) + self.assertEqual(self.my_message, self.my_enum.containing_type) + + def testContainingServiceFixups(self): + self.assertEqual(self.my_service, self.my_method.containing_service) + + def testGetOptions(self): + self.assertEqual(self.my_enum.GetOptions(), + descriptor_pb2.EnumOptions()) + self.assertEqual(self.my_enum.values[0].GetOptions(), + descriptor_pb2.EnumValueOptions()) + self.assertEqual(self.my_message.GetOptions(), + descriptor_pb2.MessageOptions()) + self.assertEqual(self.my_message.fields[0].GetOptions(), + descriptor_pb2.FieldOptions()) + self.assertEqual(self.my_method.GetOptions(), + descriptor_pb2.MethodOptions()) + self.assertEqual(self.my_service.GetOptions(), + descriptor_pb2.ServiceOptions()) + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/encoder.py b/python/google/protobuf/internal/encoder.py new file mode 100755 index 00000000..29c78b23 --- /dev/null +++ b/python/google/protobuf/internal/encoder.py @@ -0,0 +1,192 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Class for encoding protocol message primitives. + +Contains the logic for encoding every logical protocol field type +into one of the 5 physical wire types. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message +from google.protobuf.internal import wire_format +from google.protobuf.internal import output_stream + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by WireFormat from the C++ proto2 +# implementation. + + +class Encoder(object): + + """Encodes logical protocol buffer fields to the wire format.""" + + def __init__(self): + self._stream = output_stream.OutputStream() + + def ToString(self): + """Returns all values encoded in this object as a string.""" + return self._stream.ToString() + + # All the Append*() methods below first append a tag+type pair to the buffer + # before appending the specified value. + + def AppendInt32(self, field_number, value): + """Appends a 32-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarint32(value) + + def AppendInt64(self, field_number, value): + """Appends a 64-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarint64(value) + + def AppendUInt32(self, field_number, unsigned_value): + """Appends an unsigned 32-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarUInt32(unsigned_value) + + def AppendUInt64(self, field_number, unsigned_value): + """Appends an unsigned 64-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarUInt64(unsigned_value) + + def AppendSInt32(self, field_number, value): + """Appends a 32-bit integer to our buffer, zigzag-encoded and then + varint-encoded. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + zigzag_value = wire_format.ZigZagEncode(value) + self._stream.AppendVarUInt32(zigzag_value) + + def AppendSInt64(self, field_number, value): + """Appends a 64-bit integer to our buffer, zigzag-encoded and then + varint-encoded. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + zigzag_value = wire_format.ZigZagEncode(value) + self._stream.AppendVarUInt64(zigzag_value) + + def AppendFixed32(self, field_number, unsigned_value): + """Appends an unsigned 32-bit integer to our buffer, in little-endian + byte-order. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32) + self._stream.AppendLittleEndian32(unsigned_value) + + def AppendFixed64(self, field_number, unsigned_value): + """Appends an unsigned 64-bit integer to our buffer, in little-endian + byte-order. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64) + self._stream.AppendLittleEndian64(unsigned_value) + + def AppendSFixed32(self, field_number, value): + """Appends a signed 32-bit integer to our buffer, in little-endian + byte-order. + """ + sign = (value & 0x80000000) and -1 or 0 + if value >> 32 != sign: + raise message.EncodeError('SFixed32 out of range: %d' % value) + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32) + self._stream.AppendLittleEndian32(value & 0xffffffff) + + def AppendSFixed64(self, field_number, value): + """Appends a signed 64-bit integer to our buffer, in little-endian + byte-order. + """ + sign = (value & 0x8000000000000000) and -1 or 0 + if value >> 64 != sign: + raise message.EncodeError('SFixed64 out of range: %d' % value) + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64) + self._stream.AppendLittleEndian64(value & 0xffffffffffffffff) + + def AppendFloat(self, field_number, value): + """Appends a floating-point number to our buffer.""" + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32) + self._stream.AppendRawBytes(struct.pack('f', value)) + + def AppendDouble(self, field_number, value): + """Appends a double-precision floating-point number to our buffer.""" + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64) + self._stream.AppendRawBytes(struct.pack('d', value)) + + def AppendBool(self, field_number, value): + """Appends a boolean to our buffer.""" + self.AppendInt32(field_number, value) + + def AppendEnum(self, field_number, value): + """Appends an enum value to our buffer.""" + self.AppendInt32(field_number, value) + + def AppendString(self, field_number, value): + """Appends a length-prefixed string to our buffer, with the + length varint-encoded. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + self._stream.AppendVarUInt32(len(value)) + self._stream.AppendRawBytes(value) + + def AppendBytes(self, field_number, value): + """Appends a length-prefixed sequence of bytes to our buffer, with the + length varint-encoded. + """ + self.AppendString(field_number, value) + + # TODO(robinson): For AppendGroup() and AppendMessage(), we'd really like to + # avoid the extra string copy here. We can do so if we widen the Message + # interface to be able to serialize to a stream in addition to a string. The + # challenge when thinking ahead to the Python/C API implementation of Message + # is finding a stream-like Python thing to which we can write raw bytes + # from C. I'm not sure such a thing exists(?). (array.array is pretty much + # what we want, but it's not directly exposed in the Python/C API). + + def AppendGroup(self, field_number, group): + """Appends a group to our buffer. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_START_GROUP) + self._stream.AppendRawBytes(group.SerializeToString()) + self._AppendTag(field_number, wire_format.WIRETYPE_END_GROUP) + + def AppendMessage(self, field_number, msg): + """Appends a nested message to our buffer. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + self._stream.AppendVarUInt32(msg.ByteSize()) + self._stream.AppendRawBytes(msg.SerializeToString()) + + def AppendMessageSetItem(self, field_number, msg): + """Appends an item using the message set wire format. + + The message set message looks like this: + message MessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required string message = 3; + } + } + """ + self._AppendTag(1, wire_format.WIRETYPE_START_GROUP) + self.AppendInt32(2, field_number) + self.AppendMessage(3, msg) + self._AppendTag(1, wire_format.WIRETYPE_END_GROUP) + + def _AppendTag(self, field_number, wire_type): + """Appends a tag containing field number and wire type information.""" + self._stream.AppendVarUInt32(wire_format.PackTag(field_number, wire_type)) diff --git a/python/google/protobuf/internal/encoder_test.py b/python/google/protobuf/internal/encoder_test.py new file mode 100755 index 00000000..5d690da7 --- /dev/null +++ b/python/google/protobuf/internal/encoder_test.py @@ -0,0 +1,211 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test for google.protobuf.internal.encoder.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +import logging +import unittest +import mox +from google.protobuf.internal import wire_format +from google.protobuf.internal import encoder +from google.protobuf.internal import output_stream +from google.protobuf import message + + +class EncoderTest(unittest.TestCase): + + def setUp(self): + self.mox = mox.Mox() + self.encoder = encoder.Encoder() + self.mock_stream = self.mox.CreateMock(output_stream.OutputStream) + self.mock_message = self.mox.CreateMock(message.Message) + self.encoder._stream = self.mock_stream + + def PackTag(self, field_number, wire_type): + return wire_format.PackTag(field_number, wire_type) + + def AppendScalarTestHelper(self, test_name, encoder_method, + expected_stream_method_name, + wire_type, field_value, expected_value=None): + """Helper for testAppendScalars. + + Calls one of the Encoder methods, and ensures that the Encoder + in turn makes the expected calls into its OutputStream. + + Args: + test_name: Name of this test, used only for logging. + encoder_method: Callable on self.encoder, which should + accept |field_value| as an argument. This is the Encoder + method we're testing. + expected_stream_method_name: (string) Name of the OutputStream + method we expect Encoder to call to actually put the value + on the wire. + wire_type: The WIRETYPE_* constant we expect encoder to + use in the specified encoder_method. + field_value: The value we're trying to encode. Passed + into encoder_method. + expected_value: The value we expect Encoder to pass into + the OutputStream method. If None, we expect field_value + to pass through unmodified. + """ + if expected_value is None: + expected_value = field_value + + logging.info('Testing %s scalar output.\n' + 'Calling %r(%r), and expecting that to call the ' + 'stream method %s(%r).' % ( + test_name, encoder_method, field_value, + expected_stream_method_name, expected_value)) + + field_number = 10 + # Should first append the field number and type information. + self.mock_stream.AppendVarUInt32(self.PackTag(field_number, wire_type)) + # If we're length-delimited, we should then append the length. + if wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED: + self.mock_stream.AppendVarUInt32(len(field_value)) + # Should then append the value itself. + # We have to use names instead of methods to work around some + # mox weirdness. (ResetAll() is overzealous). + expected_stream_method = getattr(self.mock_stream, + expected_stream_method_name) + expected_stream_method(expected_value) + + self.mox.ReplayAll() + encoder_method(field_number, field_value) + self.mox.VerifyAll() + self.mox.ResetAll() + + def testAppendScalars(self): + scalar_tests = [ + ['int32', self.encoder.AppendInt32, 'AppendVarint32', + wire_format.WIRETYPE_VARINT, 0], + ['int64', self.encoder.AppendInt64, 'AppendVarint64', + wire_format.WIRETYPE_VARINT, 0], + ['uint32', self.encoder.AppendUInt32, 'AppendVarUInt32', + wire_format.WIRETYPE_VARINT, 0], + ['uint64', self.encoder.AppendUInt64, 'AppendVarUInt64', + wire_format.WIRETYPE_VARINT, 0], + ['fixed32', self.encoder.AppendFixed32, 'AppendLittleEndian32', + wire_format.WIRETYPE_FIXED32, 0], + ['fixed64', self.encoder.AppendFixed64, 'AppendLittleEndian64', + wire_format.WIRETYPE_FIXED64, 0], + ['sfixed32', self.encoder.AppendSFixed32, 'AppendLittleEndian32', + wire_format.WIRETYPE_FIXED32, -1, 0xffffffff], + ['sfixed64', self.encoder.AppendSFixed64, 'AppendLittleEndian64', + wire_format.WIRETYPE_FIXED64, -1, 0xffffffffffffffff], + ['float', self.encoder.AppendFloat, 'AppendRawBytes', + wire_format.WIRETYPE_FIXED32, 0.0, struct.pack('f', 0.0)], + ['double', self.encoder.AppendDouble, 'AppendRawBytes', + wire_format.WIRETYPE_FIXED64, 0.0, struct.pack('d', 0.0)], + ['bool', self.encoder.AppendBool, 'AppendVarint32', + wire_format.WIRETYPE_VARINT, False], + ['enum', self.encoder.AppendEnum, 'AppendVarint32', + wire_format.WIRETYPE_VARINT, 0], + ['string', self.encoder.AppendString, 'AppendRawBytes', + wire_format.WIRETYPE_LENGTH_DELIMITED, + "You're in a maze of twisty little passages, all alike."], + # We test zigzag encoding routines more extensively below. + ['sint32', self.encoder.AppendSInt32, 'AppendVarUInt32', + wire_format.WIRETYPE_VARINT, -1, 1], + ['sint64', self.encoder.AppendSInt64, 'AppendVarUInt64', + wire_format.WIRETYPE_VARINT, -1, 1], + ] + # Ensure that we're testing different Encoder methods and using + # different test names in all test cases above. + self.assertEqual(len(scalar_tests), len(set(t[0] for t in scalar_tests))) + self.assertEqual(len(scalar_tests), len(set(t[1] for t in scalar_tests))) + for args in scalar_tests: + self.AppendScalarTestHelper(*args) + + def testAppendGroup(self): + field_number = 23 + # Should first append the start-group marker. + self.mock_stream.AppendVarUInt32( + self.PackTag(field_number, wire_format.WIRETYPE_START_GROUP)) + # Should then serialize itself. + self.mock_message.SerializeToString().AndReturn('foo') + self.mock_stream.AppendRawBytes('foo') + # Should finally append the end-group marker. + self.mock_stream.AppendVarUInt32( + self.PackTag(field_number, wire_format.WIRETYPE_END_GROUP)) + + self.mox.ReplayAll() + self.encoder.AppendGroup(field_number, self.mock_message) + self.mox.VerifyAll() + + def testAppendMessage(self): + field_number = 23 + byte_size = 42 + # Should first append the field number and type information. + self.mock_stream.AppendVarUInt32( + self.PackTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)) + # Should then append its length. + self.mock_message.ByteSize().AndReturn(byte_size) + self.mock_stream.AppendVarUInt32(byte_size) + # Should then serialize itself to the encoder. + self.mock_message.SerializeToString().AndReturn('foo') + self.mock_stream.AppendRawBytes('foo') + + self.mox.ReplayAll() + self.encoder.AppendMessage(field_number, self.mock_message) + self.mox.VerifyAll() + + def testAppendMessageSetItem(self): + field_number = 23 + byte_size = 42 + # Should first append the field number and type information. + self.mock_stream.AppendVarUInt32( + self.PackTag(1, wire_format.WIRETYPE_START_GROUP)) + self.mock_stream.AppendVarUInt32( + self.PackTag(2, wire_format.WIRETYPE_VARINT)) + self.mock_stream.AppendVarint32(field_number) + self.mock_stream.AppendVarUInt32( + self.PackTag(3, wire_format.WIRETYPE_LENGTH_DELIMITED)) + # Should then append its length. + self.mock_message.ByteSize().AndReturn(byte_size) + self.mock_stream.AppendVarUInt32(byte_size) + # Should then serialize itself to the encoder. + self.mock_message.SerializeToString().AndReturn('foo') + self.mock_stream.AppendRawBytes('foo') + self.mock_stream.AppendVarUInt32( + self.PackTag(1, wire_format.WIRETYPE_END_GROUP)) + + self.mox.ReplayAll() + self.encoder.AppendMessageSetItem(field_number, self.mock_message) + self.mox.VerifyAll() + + def testAppendSFixed(self): + # Most of our bounds-checking is done in output_stream.py, + # but encoder.py is responsible for transforming signed + # fixed-width integers into unsigned ones, so we test here + # to ensure that we're not losing any entropy when we do + # that conversion. + field_number = 10 + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed32, + 10, wire_format.UINT32_MAX + 1) + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed32, + 10, -(1 << 32)) + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed64, + 10, wire_format.UINT64_MAX + 1) + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed64, + 10, -(1 << 64)) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py new file mode 100755 index 00000000..02f993f7 --- /dev/null +++ b/python/google/protobuf/internal/generator_test.py @@ -0,0 +1,84 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO(robinson): Flesh this out considerably. We focused on reflection_test.py +# first, since it's testing the subtler code, and since it provides decent +# indirect testing of the protocol compiler output. + +"""Unittest that directly tests the output of the pure-Python protocol +compiler. See //net/proto2/internal/reflection_test.py for a test which +further ensures that we can use Python protocol message objects as we expect. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import unittest_mset_pb2 +from google.protobuf import unittest_pb2 + + +class GeneratorTest(unittest.TestCase): + + def testNestedMessageDescriptor(self): + field_name = 'optional_nested_message' + proto_type = unittest_pb2.TestAllTypes + self.assertEqual( + proto_type.NestedMessage.DESCRIPTOR, + proto_type.DESCRIPTOR.fields_by_name[field_name].message_type) + + def testEnums(self): + # We test only module-level enums here. + # TODO(robinson): Examine descriptors directly to check + # enum descriptor output. + self.assertEqual(4, unittest_pb2.FOREIGN_FOO) + self.assertEqual(5, unittest_pb2.FOREIGN_BAR) + self.assertEqual(6, unittest_pb2.FOREIGN_BAZ) + + proto = unittest_pb2.TestAllTypes() + self.assertEqual(1, proto.FOO) + self.assertEqual(1, unittest_pb2.TestAllTypes.FOO) + self.assertEqual(2, proto.BAR) + self.assertEqual(2, unittest_pb2.TestAllTypes.BAR) + self.assertEqual(3, proto.BAZ) + self.assertEqual(3, unittest_pb2.TestAllTypes.BAZ) + + def testContainingTypeBehaviorForExtensions(self): + self.assertEqual(unittest_pb2.optional_int32_extension.containing_type, + unittest_pb2.TestAllExtensions.DESCRIPTOR) + self.assertEqual(unittest_pb2.TestRequired.single.containing_type, + unittest_pb2.TestAllExtensions.DESCRIPTOR) + + def testExtensionScope(self): + self.assertEqual(unittest_pb2.optional_int32_extension.extension_scope, + None) + self.assertEqual(unittest_pb2.TestRequired.single.extension_scope, + unittest_pb2.TestRequired.DESCRIPTOR) + + def testIsExtension(self): + self.assertTrue(unittest_pb2.optional_int32_extension.is_extension) + self.assertTrue(unittest_pb2.TestRequired.single.is_extension) + + message_descriptor = unittest_pb2.TestRequired.DESCRIPTOR + non_extension_descriptor = message_descriptor.fields_by_name['a'] + self.assertTrue(not non_extension_descriptor.is_extension) + + def testOptions(self): + proto = unittest_mset_pb2.TestMessageSet() + self.assertTrue(proto.DESCRIPTOR.GetOptions().message_set_wire_format) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/input_stream.py b/python/google/protobuf/internal/input_stream.py new file mode 100755 index 00000000..9f3b0f5a --- /dev/null +++ b/python/google/protobuf/internal/input_stream.py @@ -0,0 +1,211 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""InputStream is the primitive interface for reading bits from the wire. + +All protocol buffer deserialization can be expressed in terms of +the InputStream primitives provided here. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message +from google.protobuf.internal import wire_format + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by CodedInputStream from the C++ +# proto2 implementation. + + +class InputStream(object): + + """Contains all logic for reading bits, and dealing with stream position. + + If an InputStream method ever raises an exception, the stream is left + in an indeterminate state and is not safe for further use. + """ + + def __init__(self, s): + # What we really want is something like array('B', s), where elements we + # read from the array are already given to us as one-byte integers. BUT + # using array() instead of buffer() would force full string copies to result + # from each GetSubBuffer() call. + # + # So, if the N serialized bytes of a single protocol buffer object are + # split evenly between 2 child messages, and so on recursively, using + # array('B', s) instead of buffer() would incur an additional N*logN bytes + # copied during deserialization. + # + # The higher constant overhead of having to ord() for every byte we read + # from the buffer in _ReadVarintHelper() could definitely lead to worse + # performance in many real-world scenarios, even if the asymptotic + # complexity is better. However, our real answer is that the mythical + # Python/C extension module output mode for the protocol compiler will + # be blazing-fast and will eliminate most use of this class anyway. + self._buffer = buffer(s) + self._pos = 0 + + def EndOfStream(self): + """Returns true iff we're at the end of the stream. + If this returns true, then a call to any other InputStream method + will raise an exception. + """ + return self._pos >= len(self._buffer) + + def Position(self): + """Returns the current position in the stream, or equivalently, the + number of bytes read so far. + """ + return self._pos + + def GetSubBuffer(self, size=None): + """Returns a sequence-like object that represents a portion of our + underlying sequence. + + Position 0 in the returned object corresponds to self.Position() + in this stream. + + If size is specified, then the returned object ends after the + next "size" bytes in this stream. If size is not specified, + then the returned object ends at the end of this stream. + + We guarantee that the returned object R supports the Python buffer + interface (and thus that the call buffer(R) will work). + + Note that the returned buffer is read-only. + + The intended use for this method is for nested-message and nested-group + deserialization, where we want to make a recursive MergeFromString() + call on the portion of the original sequence that contains the serialized + nested message. (And we'd like to do so without making unnecessary string + copies). + + REQUIRES: size is nonnegative. + """ + # Note that buffer() doesn't perform any actual string copy. + if size is None: + return buffer(self._buffer, self._pos) + else: + if size < 0: + raise message.DecodeError('Negative size %d' % size) + return buffer(self._buffer, self._pos, size) + + def SkipBytes(self, num_bytes): + """Skip num_bytes bytes ahead, or go to the end of the stream, whichever + comes first. + + REQUIRES: num_bytes is nonnegative. + """ + if num_bytes < 0: + raise message.DecodeError('Negative num_bytes %d' % num_bytes) + self._pos += num_bytes + self._pos = min(self._pos, len(self._buffer)) + + def ReadString(self, size): + """Reads up to 'size' bytes from the stream, stopping early + only if we reach the end of the stream. Returns the bytes read + as a string. + """ + if size < 0: + raise message.DecodeError('Negative size %d' % size) + s = (self._buffer[self._pos : self._pos + size]) + self._pos += len(s) # Only advance by the number of bytes actually read. + return s + + def ReadLittleEndian32(self): + """Interprets the next 4 bytes of the stream as a little-endian + encoded, unsiged 32-bit integer, and returns that integer. + """ + try: + i = struct.unpack(wire_format.FORMAT_UINT32_LITTLE_ENDIAN, + self._buffer[self._pos : self._pos + 4]) + self._pos += 4 + return i[0] # unpack() result is a 1-element tuple. + except struct.error, e: + raise message.DecodeError(e) + + def ReadLittleEndian64(self): + """Interprets the next 8 bytes of the stream as a little-endian + encoded, unsiged 64-bit integer, and returns that integer. + """ + try: + i = struct.unpack(wire_format.FORMAT_UINT64_LITTLE_ENDIAN, + self._buffer[self._pos : self._pos + 8]) + self._pos += 8 + return i[0] # unpack() result is a 1-element tuple. + except struct.error, e: + raise message.DecodeError(e) + + def ReadVarint32(self): + """Reads a varint from the stream, interprets this varint + as a signed, 32-bit integer, and returns the integer. + """ + i = self.ReadVarint64() + if not wire_format.INT32_MIN <= i <= wire_format.INT32_MAX: + raise message.DecodeError('Value out of range for int32: %d' % i) + return int(i) + + def ReadVarUInt32(self): + """Reads a varint from the stream, interprets this varint + as an unsigned, 32-bit integer, and returns the integer. + """ + i = self.ReadVarUInt64() + if i > wire_format.UINT32_MAX: + raise message.DecodeError('Value out of range for uint32: %d' % i) + return i + + def ReadVarint64(self): + """Reads a varint from the stream, interprets this varint + as a signed, 64-bit integer, and returns the integer. + """ + i = self.ReadVarUInt64() + if i > wire_format.INT64_MAX: + i -= (1 << 64) + return i + + def ReadVarUInt64(self): + """Reads a varint from the stream, interprets this varint + as an unsigned, 64-bit integer, and returns the integer. + """ + i = self._ReadVarintHelper() + if not 0 <= i <= wire_format.UINT64_MAX: + raise message.DecodeError('Value out of range for uint64: %d' % i) + return i + + def _ReadVarintHelper(self): + """Helper for the various varint-reading methods above. + Reads an unsigned, varint-encoded integer from the stream and + returns this integer. + + Does no bounds checking except to ensure that we read at most as many bytes + as could possibly be present in a varint-encoded 64-bit number. + """ + result = 0 + shift = 0 + while 1: + if shift >= 64: + raise message.DecodeError('Too many bytes when decoding varint.') + try: + b = ord(self._buffer[self._pos]) + except IndexError: + raise message.DecodeError('Truncated varint.') + self._pos += 1 + result |= ((b & 0x7f) << shift) + shift += 7 + if not (b & 0x80): + return result diff --git a/python/google/protobuf/internal/input_stream_test.py b/python/google/protobuf/internal/input_stream_test.py new file mode 100755 index 00000000..2d685545 --- /dev/null +++ b/python/google/protobuf/internal/input_stream_test.py @@ -0,0 +1,279 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test for google.protobuf.internal.input_stream.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import message +from google.protobuf.internal import wire_format +from google.protobuf.internal import input_stream + + +class InputStreamTest(unittest.TestCase): + + def testEndOfStream(self): + stream = input_stream.InputStream('abcd') + self.assertFalse(stream.EndOfStream()) + self.assertEqual('abcd', stream.ReadString(10)) + self.assertTrue(stream.EndOfStream()) + + def testPosition(self): + stream = input_stream.InputStream('abcd') + self.assertEqual(0, stream.Position()) + self.assertEqual(0, stream.Position()) # No side-effects. + stream.ReadString(1) + self.assertEqual(1, stream.Position()) + stream.ReadString(1) + self.assertEqual(2, stream.Position()) + stream.ReadString(10) + self.assertEqual(4, stream.Position()) # Can't go past end of stream. + + def testGetSubBuffer(self): + stream = input_stream.InputStream('abcd') + # Try leaving out the size. + self.assertEqual('abcd', str(stream.GetSubBuffer())) + stream.SkipBytes(1) + # GetSubBuffer() always starts at current size. + self.assertEqual('bcd', str(stream.GetSubBuffer())) + # Try 0-size. + self.assertEqual('', str(stream.GetSubBuffer(0))) + # Negative sizes should raise an error. + self.assertRaises(message.DecodeError, stream.GetSubBuffer, -1) + # Positive sizes should work as expected. + self.assertEqual('b', str(stream.GetSubBuffer(1))) + self.assertEqual('bc', str(stream.GetSubBuffer(2))) + # Sizes longer than remaining bytes in the buffer should + # return the whole remaining buffer. + self.assertEqual('bcd', str(stream.GetSubBuffer(1000))) + + def testSkipBytes(self): + stream = input_stream.InputStream('') + # Skipping bytes when at the end of stream + # should have no effect. + stream.SkipBytes(0) + stream.SkipBytes(1) + stream.SkipBytes(2) + self.assertTrue(stream.EndOfStream()) + self.assertEqual(0, stream.Position()) + + # Try skipping within a stream. + stream = input_stream.InputStream('abcd') + self.assertEqual(0, stream.Position()) + stream.SkipBytes(1) + self.assertEqual(1, stream.Position()) + stream.SkipBytes(10) # Can't skip past the end. + self.assertEqual(4, stream.Position()) + + # Ensure that a negative skip raises an exception. + stream = input_stream.InputStream('abcd') + stream.SkipBytes(1) + self.assertRaises(message.DecodeError, stream.SkipBytes, -1) + + def testReadString(self): + s = 'abcd' + # Also test going past the total stream length. + for i in range(len(s) + 10): + stream = input_stream.InputStream(s) + self.assertEqual(s[:i], stream.ReadString(i)) + self.assertEqual(min(i, len(s)), stream.Position()) + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadString, -1) + + def EnsureFailureOnEmptyStream(self, input_stream_method): + """Helper for integer-parsing tests below. + Ensures that the given InputStream method raises a DecodeError + if called on a stream with no bytes remaining. + """ + stream = input_stream.InputStream('') + self.assertRaises(message.DecodeError, input_stream_method, stream) + + def testReadLittleEndian32(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadLittleEndian32) + s = '' + # Read 0. + s += '\x00\x00\x00\x00' + # Read 1. + s += '\x01\x00\x00\x00' + # Read a bunch of different bytes. + s += '\x01\x02\x03\x04' + # Read max unsigned 32-bit int. + s += '\xff\xff\xff\xff' + # Try a read with fewer than 4 bytes left in the stream. + s += '\x00\x00\x00' + stream = input_stream.InputStream(s) + self.assertEqual(0, stream.ReadLittleEndian32()) + self.assertEqual(4, stream.Position()) + self.assertEqual(1, stream.ReadLittleEndian32()) + self.assertEqual(8, stream.Position()) + self.assertEqual(0x04030201, stream.ReadLittleEndian32()) + self.assertEqual(12, stream.Position()) + self.assertEqual(wire_format.UINT32_MAX, stream.ReadLittleEndian32()) + self.assertEqual(16, stream.Position()) + self.assertRaises(message.DecodeError, stream.ReadLittleEndian32) + + def testReadLittleEndian64(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadLittleEndian64) + s = '' + # Read 0. + s += '\x00\x00\x00\x00\x00\x00\x00\x00' + # Read 1. + s += '\x01\x00\x00\x00\x00\x00\x00\x00' + # Read a bunch of different bytes. + s += '\x01\x02\x03\x04\x05\x06\x07\x08' + # Read max unsigned 64-bit int. + s += '\xff\xff\xff\xff\xff\xff\xff\xff' + # Try a read with fewer than 8 bytes left in the stream. + s += '\x00\x00\x00' + stream = input_stream.InputStream(s) + self.assertEqual(0, stream.ReadLittleEndian64()) + self.assertEqual(8, stream.Position()) + self.assertEqual(1, stream.ReadLittleEndian64()) + self.assertEqual(16, stream.Position()) + self.assertEqual(0x0807060504030201, stream.ReadLittleEndian64()) + self.assertEqual(24, stream.Position()) + self.assertEqual(wire_format.UINT64_MAX, stream.ReadLittleEndian64()) + self.assertEqual(32, stream.Position()) + self.assertRaises(message.DecodeError, stream.ReadLittleEndian64) + + def ReadVarintSuccessTestHelper(self, varints_and_ints, read_method): + """Helper for tests below that test successful reads of various varints. + + Args: + varints_and_ints: Iterable of (str, integer) pairs, where the string + gives the wire encoding and the integer gives the value we expect + to be returned by the read_method upon encountering this string. + read_method: Unbound InputStream method that is capable of reading + the encoded strings provided in the first elements of varints_and_ints. + """ + s = ''.join(s for s, i in varints_and_ints) + stream = input_stream.InputStream(s) + expected_pos = 0 + self.assertEqual(expected_pos, stream.Position()) + for s, expected_int in varints_and_ints: + self.assertEqual(expected_int, read_method(stream)) + expected_pos += len(s) + self.assertEqual(expected_pos, stream.Position()) + + def testReadVarint32Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01', -1), + ('\xff\xff\xff\xff\x07', wire_format.INT32_MAX), + ('\x80\x80\x80\x80\xf8\xff\xff\xff\xff\x01', wire_format.INT32_MIN), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarint32) + + def testReadVarint32Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarint32) + + # Try and fail to read INT32_MAX + 1. + s = '\x80\x80\x80\x80\x08' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint32) + + # Try and fail to read INT32_MIN - 1. + s = '\xfe\xff\xff\xff\xf7\xff\xff\xff\xff\x01' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint32) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint32) + + def testReadVarUInt32Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\xff\xff\xff\xff\x0f', wire_format.UINT32_MAX), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarUInt32) + + def testReadVarUInt32Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarUInt32) + # Try and fail to read UINT32_MAX + 1 + s = '\x80\x80\x80\x80\x10' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt32) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt32) + + def testReadVarint64Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01', -1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\xff\xff\xff\xff\xff\xff\xff\xff\x7f', wire_format.INT64_MAX), + ('\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01', wire_format.INT64_MIN), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarint64) + + def testReadVarint64Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarint64) + # Try and fail to read something with the mythical 64th bit set. + s = '\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint64) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint64) + + def testReadVarUInt64Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01', 1 << 63), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarUInt64) + + def testReadVarUInt64Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarUInt64) + # Try and fail to read something with the mythical 64th bit set. + s = '\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt64) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt64) + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/message_listener.py b/python/google/protobuf/internal/message_listener.py new file mode 100755 index 00000000..3747909e --- /dev/null +++ b/python/google/protobuf/internal/message_listener.py @@ -0,0 +1,55 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Defines a listener interface for observing certain +state transitions on Message objects. + +Also defines a null implementation of this interface. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + + +class MessageListener(object): + + """Listens for transitions to nonempty and for invalidations of cached + byte sizes. Meant to be registered via Message._SetListener(). + """ + + def TransitionToNonempty(self): + """Called the *first* time that this message becomes nonempty. + Implementations are free (but not required) to call this method multiple + times after the message has become nonempty. + """ + raise NotImplementedError + + def ByteSizeDirty(self): + """Called *every* time the cached byte size value + for this object is invalidated (transitions from being + "clean" to "dirty"). + """ + raise NotImplementedError + + +class NullMessageListener(object): + + """No-op MessageListener implementation.""" + + def TransitionToNonempty(self): + pass + + def ByteSizeDirty(self): + pass diff --git a/python/google/protobuf/internal/more_extensions.proto b/python/google/protobuf/internal/more_extensions.proto new file mode 100644 index 00000000..48df6f55 --- /dev/null +++ b/python/google/protobuf/internal/more_extensions.proto @@ -0,0 +1,44 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: robinson@google.com (Will Robinson) + + +package google.protobuf.internal; + + +message TopLevelMessage { + optional ExtendedMessage submessage = 1; +} + + +message ExtendedMessage { + extensions 1 to max; +} + + +message ForeignMessage { + optional int32 foreign_message_int = 1; +} + + +extend ExtendedMessage { + optional int32 optional_int_extension = 1; + optional ForeignMessage optional_message_extension = 2; + + repeated int32 repeated_int_extension = 3; + repeated ForeignMessage repeated_message_extension = 4; +} diff --git a/python/google/protobuf/internal/more_messages.proto b/python/google/protobuf/internal/more_messages.proto new file mode 100644 index 00000000..bfa12273 --- /dev/null +++ b/python/google/protobuf/internal/more_messages.proto @@ -0,0 +1,37 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: robinson@google.com (Will Robinson) + + +package google.protobuf.internal; + +// A message where tag numbers are listed out of order, to allow us to test our +// canonicalization of serialized output, which should always be in tag order. +// We also mix in some extensions for extra fun. +message OutOfOrderFields { + optional sint32 optional_sint32 = 5; + extensions 4 to 4; + optional uint32 optional_uint32 = 3; + extensions 2 to 2; + optional int32 optional_int32 = 1; +}; + + +extend OutOfOrderFields { + optional uint64 optional_uint64 = 4; + optional int64 optional_int64 = 2; +} diff --git a/python/google/protobuf/internal/output_stream.py b/python/google/protobuf/internal/output_stream.py new file mode 100755 index 00000000..767e9725 --- /dev/null +++ b/python/google/protobuf/internal/output_stream.py @@ -0,0 +1,112 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""OutputStream is the primitive interface for sticking bits on the wire. + +All protocol buffer serialization can be expressed in terms of +the OutputStream primitives provided here. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import array +import struct +from google.protobuf import message +from google.protobuf.internal import wire_format + + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by CodedOutputStream from the C++ +# proto2 implementation. + + +class OutputStream(object): + + """Contains all logic for writing bits, and ToString() to get the result.""" + + def __init__(self): + self._buffer = array.array('B') + + def AppendRawBytes(self, raw_bytes): + """Appends raw_bytes to our internal buffer.""" + self._buffer.fromstring(raw_bytes) + + def AppendLittleEndian32(self, unsigned_value): + """Appends an unsigned 32-bit integer to the internal buffer, + in little-endian byte order. + """ + if not 0 <= unsigned_value <= wire_format.UINT32_MAX: + raise message.EncodeError( + 'Unsigned 32-bit out of range: %d' % unsigned_value) + self._buffer.fromstring(struct.pack( + wire_format.FORMAT_UINT32_LITTLE_ENDIAN, unsigned_value)) + + def AppendLittleEndian64(self, unsigned_value): + """Appends an unsigned 64-bit integer to the internal buffer, + in little-endian byte order. + """ + if not 0 <= unsigned_value <= wire_format.UINT64_MAX: + raise message.EncodeError( + 'Unsigned 64-bit out of range: %d' % unsigned_value) + self._buffer.fromstring(struct.pack( + wire_format.FORMAT_UINT64_LITTLE_ENDIAN, unsigned_value)) + + def AppendVarint32(self, value): + """Appends a signed 32-bit integer to the internal buffer, + encoded as a varint. (Note that a negative varint32 will + always require 10 bytes of space.) + """ + if not wire_format.INT32_MIN <= value <= wire_format.INT32_MAX: + raise message.EncodeError('Value out of range: %d' % value) + self.AppendVarint64(value) + + def AppendVarUInt32(self, value): + """Appends an unsigned 32-bit integer to the internal buffer, + encoded as a varint. + """ + if not 0 <= value <= wire_format.UINT32_MAX: + raise message.EncodeError('Value out of range: %d' % value) + self.AppendVarUInt64(value) + + def AppendVarint64(self, value): + """Appends a signed 64-bit integer to the internal buffer, + encoded as a varint. + """ + if not wire_format.INT64_MIN <= value <= wire_format.INT64_MAX: + raise message.EncodeError('Value out of range: %d' % value) + if value < 0: + value += (1 << 64) + self.AppendVarUInt64(value) + + def AppendVarUInt64(self, unsigned_value): + """Appends an unsigned 64-bit integer to the internal buffer, + encoded as a varint. + """ + if not 0 <= unsigned_value <= wire_format.UINT64_MAX: + raise message.EncodeError('Value out of range: %d' % unsigned_value) + while True: + bits = unsigned_value & 0x7f + unsigned_value >>= 7 + if unsigned_value: + bits |= 0x80 + self._buffer.append(bits) + if not unsigned_value: + break + + def ToString(self): + """Returns a string containing the bytes in our internal buffer.""" + return self._buffer.tostring() diff --git a/python/google/protobuf/internal/output_stream_test.py b/python/google/protobuf/internal/output_stream_test.py new file mode 100755 index 00000000..026f6161 --- /dev/null +++ b/python/google/protobuf/internal/output_stream_test.py @@ -0,0 +1,162 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test for google.protobuf.internal.output_stream.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import message +from google.protobuf.internal import output_stream +from google.protobuf.internal import wire_format + + +class OutputStreamTest(unittest.TestCase): + + def setUp(self): + self.stream = output_stream.OutputStream() + + def testAppendRawBytes(self): + # Empty string. + self.stream.AppendRawBytes('') + self.assertEqual('', self.stream.ToString()) + + # Nonempty string. + self.stream.AppendRawBytes('abc') + self.assertEqual('abc', self.stream.ToString()) + + # Ensure that we're actually appending. + self.stream.AppendRawBytes('def') + self.assertEqual('abcdef', self.stream.ToString()) + + def AppendNumericTestHelper(self, append_fn, values_and_strings): + """For each (value, expected_string) pair in values_and_strings, + calls an OutputStream.Append*(value) method on an OutputStream and ensures + that the string written to that stream matches expected_string. + + Args: + append_fn: Unbound OutputStream method that takes an integer or + long value as input. + values_and_strings: Iterable of (value, expected_string) pairs. + """ + for conversion in (int, long): + for value, string in values_and_strings: + stream = output_stream.OutputStream() + expected_string = '' + append_fn(stream, conversion(value)) + expected_string += string + self.assertEqual(expected_string, stream.ToString()) + + def AppendOverflowTestHelper(self, append_fn, value): + """Calls an OutputStream.Append*(value) method and asserts + that the method raises message.EncodeError. + + Args: + append_fn: Unbound OutputStream method that takes an integer or + long value as input. + value: Value to pass to append_fn which should cause an + message.EncodeError. + """ + stream = output_stream.OutputStream() + self.assertRaises(message.EncodeError, append_fn, stream, value) + + def testAppendLittleEndian32(self): + append_fn = output_stream.OutputStream.AppendLittleEndian32 + values_and_expected_strings = [ + (0, '\x00\x00\x00\x00'), + (1, '\x01\x00\x00\x00'), + ((1 << 32) - 1, '\xff\xff\xff\xff'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, 1 << 32) + self.AppendOverflowTestHelper(append_fn, -1) + + def testAppendLittleEndian64(self): + append_fn = output_stream.OutputStream.AppendLittleEndian64 + values_and_expected_strings = [ + (0, '\x00\x00\x00\x00\x00\x00\x00\x00'), + (1, '\x01\x00\x00\x00\x00\x00\x00\x00'), + ((1 << 64) - 1, '\xff\xff\xff\xff\xff\xff\xff\xff'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, 1 << 64) + self.AppendOverflowTestHelper(append_fn, -1) + + def testAppendVarint32(self): + append_fn = output_stream.OutputStream.AppendVarint32 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (-1, '\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01'), + (wire_format.INT32_MAX, '\xff\xff\xff\xff\x07'), + (wire_format.INT32_MIN, '\x80\x80\x80\x80\xf8\xff\xff\xff\xff\x01'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, wire_format.INT32_MAX + 1) + self.AppendOverflowTestHelper(append_fn, wire_format.INT32_MIN - 1) + + def testAppendVarUInt32(self): + append_fn = output_stream.OutputStream.AppendVarUInt32 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (wire_format.UINT32_MAX, '\xff\xff\xff\xff\x0f'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, -1) + self.AppendOverflowTestHelper(append_fn, wire_format.UINT32_MAX + 1) + + def testAppendVarint64(self): + append_fn = output_stream.OutputStream.AppendVarint64 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (-1, '\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01'), + (wire_format.INT64_MAX, '\xff\xff\xff\xff\xff\xff\xff\xff\x7f'), + (wire_format.INT64_MIN, '\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, wire_format.INT64_MAX + 1) + self.AppendOverflowTestHelper(append_fn, wire_format.INT64_MIN - 1) + + def testAppendVarUInt64(self): + append_fn = output_stream.OutputStream.AppendVarUInt64 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (wire_format.UINT64_MAX, '\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, -1) + self.AppendOverflowTestHelper(append_fn, wire_format.UINT64_MAX + 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py new file mode 100755 index 00000000..5947f97a --- /dev/null +++ b/python/google/protobuf/internal/reflection_test.py @@ -0,0 +1,1300 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unittest for reflection.py, which also indirectly tests the output of the +pure-Python protocol compiler. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import operator + +import unittest +# TODO(robinson): When we split this test in two, only some of these imports +# will be necessary in each test. +from google.protobuf import unittest_import_pb2 +from google.protobuf import unittest_mset_pb2 +from google.protobuf import unittest_pb2 +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor +from google.protobuf import message +from google.protobuf import reflection +from google.protobuf.internal import more_extensions_pb2 +from google.protobuf.internal import more_messages_pb2 +from google.protobuf.internal import wire_format +from google.protobuf.internal import test_util +from google.protobuf.internal import decoder + + +class RefectionTest(unittest.TestCase): + + def testSimpleHasBits(self): + # Test a scalar. + proto = unittest_pb2.TestAllTypes() + self.assertTrue(not proto.HasField('optional_int32')) + self.assertEqual(0, proto.optional_int32) + # HasField() shouldn't be true if all we've done is + # read the default value. + self.assertTrue(not proto.HasField('optional_int32')) + proto.optional_int32 = 1 + # Setting a value however *should* set the "has" bit. + self.assertTrue(proto.HasField('optional_int32')) + proto.ClearField('optional_int32') + # And clearing that value should unset the "has" bit. + self.assertTrue(not proto.HasField('optional_int32')) + + def testHasBitsWithSinglyNestedScalar(self): + # Helper used to test foreign messages and groups. + # + # composite_field_name should be the name of a non-repeated + # composite (i.e., foreign or group) field in TestAllTypes, + # and scalar_field_name should be the name of an integer-valued + # scalar field within that composite. + # + # I never thought I'd miss C++ macros and templates so much. :( + # This helper is semantically just: + # + # assert proto.composite_field.scalar_field == 0 + # assert not proto.composite_field.HasField('scalar_field') + # assert not proto.HasField('composite_field') + # + # proto.composite_field.scalar_field = 10 + # old_composite_field = proto.composite_field + # + # assert proto.composite_field.scalar_field == 10 + # assert proto.composite_field.HasField('scalar_field') + # assert proto.HasField('composite_field') + # + # proto.ClearField('composite_field') + # + # assert not proto.composite_field.HasField('scalar_field') + # assert not proto.HasField('composite_field') + # assert proto.composite_field.scalar_field == 0 + # + # # Now ensure that ClearField('composite_field') disconnected + # # the old field object from the object tree... + # assert old_composite_field is not proto.composite_field + # old_composite_field.scalar_field = 20 + # assert not proto.composite_field.HasField('scalar_field') + # assert not proto.HasField('composite_field') + def TestCompositeHasBits(composite_field_name, scalar_field_name): + proto = unittest_pb2.TestAllTypes() + # First, check that we can get the scalar value, and see that it's the + # default (0), but that proto.HasField('omposite') and + # proto.composite.HasField('scalar') will still return False. + composite_field = getattr(proto, composite_field_name) + original_scalar_value = getattr(composite_field, scalar_field_name) + self.assertEqual(0, original_scalar_value) + # Assert that the composite object does not "have" the scalar. + self.assertTrue(not composite_field.HasField(scalar_field_name)) + # Assert that proto does not "have" the composite field. + self.assertTrue(not proto.HasField(composite_field_name)) + + # Now set the scalar within the composite field. Ensure that the setting + # is reflected, and that proto.HasField('composite') and + # proto.composite.HasField('scalar') now both return True. + new_val = 20 + setattr(composite_field, scalar_field_name, new_val) + self.assertEqual(new_val, getattr(composite_field, scalar_field_name)) + # Hold on to a reference to the current composite_field object. + old_composite_field = composite_field + # Assert that the has methods now return true. + self.assertTrue(composite_field.HasField(scalar_field_name)) + self.assertTrue(proto.HasField(composite_field_name)) + + # Now call the clear method... + proto.ClearField(composite_field_name) + + # ...and ensure that the "has" bits are all back to False... + composite_field = getattr(proto, composite_field_name) + self.assertTrue(not composite_field.HasField(scalar_field_name)) + self.assertTrue(not proto.HasField(composite_field_name)) + # ...and ensure that the scalar field has returned to its default. + self.assertEqual(0, getattr(composite_field, scalar_field_name)) + + # Finally, ensure that modifications to the old composite field object + # don't have any effect on the parent. + # + # (NOTE that when we clear the composite field in the parent, we actually + # don't recursively clear down the tree. Instead, we just disconnect the + # cleared composite from the tree.) + self.assertTrue(old_composite_field is not composite_field) + setattr(old_composite_field, scalar_field_name, new_val) + self.assertTrue(not composite_field.HasField(scalar_field_name)) + self.assertTrue(not proto.HasField(composite_field_name)) + self.assertEqual(0, getattr(composite_field, scalar_field_name)) + + # Test simple, single-level nesting when we set a scalar. + TestCompositeHasBits('optionalgroup', 'a') + TestCompositeHasBits('optional_nested_message', 'bb') + TestCompositeHasBits('optional_foreign_message', 'c') + TestCompositeHasBits('optional_import_message', 'd') + + def testReferencesToNestedMessage(self): + proto = unittest_pb2.TestAllTypes() + nested = proto.optional_nested_message + del proto + # A previous version had a bug where this would raise an exception when + # hitting a now-dead weak reference. + nested.bb = 23 + + def testDisconnectingNestedMessageBeforeSettingField(self): + proto = unittest_pb2.TestAllTypes() + nested = proto.optional_nested_message + proto.ClearField('optional_nested_message') # Should disconnect from parent + self.assertTrue(nested is not proto.optional_nested_message) + nested.bb = 23 + self.assertTrue(not proto.HasField('optional_nested_message')) + self.assertEqual(0, proto.optional_nested_message.bb) + + def testHasBitsWhenModifyingRepeatedFields(self): + # Test nesting when we add an element to a repeated field in a submessage. + proto = unittest_pb2.TestNestedMessageHasBits() + proto.optional_nested_message.nestedmessage_repeated_int32.append(5) + self.assertEqual( + [5], proto.optional_nested_message.nestedmessage_repeated_int32) + self.assertTrue(proto.HasField('optional_nested_message')) + + # Do the same test, but with a repeated composite field within the + # submessage. + proto.ClearField('optional_nested_message') + self.assertTrue(not proto.HasField('optional_nested_message')) + proto.optional_nested_message.nestedmessage_repeated_foreignmessage.add() + self.assertTrue(proto.HasField('optional_nested_message')) + + def testHasBitsForManyLevelsOfNesting(self): + # Test nesting many levels deep. + recursive_proto = unittest_pb2.TestMutualRecursionA() + self.assertTrue(not recursive_proto.HasField('bb')) + self.assertEqual(0, recursive_proto.bb.a.bb.a.bb.optional_int32) + self.assertTrue(not recursive_proto.HasField('bb')) + recursive_proto.bb.a.bb.a.bb.optional_int32 = 5 + self.assertEqual(5, recursive_proto.bb.a.bb.a.bb.optional_int32) + self.assertTrue(recursive_proto.HasField('bb')) + self.assertTrue(recursive_proto.bb.HasField('a')) + self.assertTrue(recursive_proto.bb.a.HasField('bb')) + self.assertTrue(recursive_proto.bb.a.bb.HasField('a')) + self.assertTrue(recursive_proto.bb.a.bb.a.HasField('bb')) + self.assertTrue(not recursive_proto.bb.a.bb.a.bb.HasField('a')) + self.assertTrue(recursive_proto.bb.a.bb.a.bb.HasField('optional_int32')) + + def testSingularListFields(self): + proto = unittest_pb2.TestAllTypes() + proto.optional_fixed32 = 1 + proto.optional_int32 = 5 + proto.optional_string = 'foo' + self.assertEqual( + [ (proto.DESCRIPTOR.fields_by_name['optional_int32' ], 5), + (proto.DESCRIPTOR.fields_by_name['optional_fixed32'], 1), + (proto.DESCRIPTOR.fields_by_name['optional_string' ], 'foo') ], + proto.ListFields()) + + def testRepeatedListFields(self): + proto = unittest_pb2.TestAllTypes() + proto.repeated_fixed32.append(1) + proto.repeated_int32.append(5) + proto.repeated_int32.append(11) + proto.repeated_string.append('foo') + proto.repeated_string.append('bar') + proto.repeated_string.append('baz') + proto.optional_int32 = 21 + self.assertEqual( + [ (proto.DESCRIPTOR.fields_by_name['optional_int32' ], 21), + (proto.DESCRIPTOR.fields_by_name['repeated_int32' ], [5, 11]), + (proto.DESCRIPTOR.fields_by_name['repeated_fixed32'], [1]), + (proto.DESCRIPTOR.fields_by_name['repeated_string' ], + ['foo', 'bar', 'baz']) ], + proto.ListFields()) + + def testSingularListExtensions(self): + proto = unittest_pb2.TestAllExtensions() + proto.Extensions[unittest_pb2.optional_fixed32_extension] = 1 + proto.Extensions[unittest_pb2.optional_int32_extension ] = 5 + proto.Extensions[unittest_pb2.optional_string_extension ] = 'foo' + self.assertEqual( + [ (unittest_pb2.optional_int32_extension , 5), + (unittest_pb2.optional_fixed32_extension, 1), + (unittest_pb2.optional_string_extension , 'foo') ], + proto.ListFields()) + + def testRepeatedListExtensions(self): + proto = unittest_pb2.TestAllExtensions() + proto.Extensions[unittest_pb2.repeated_fixed32_extension].append(1) + proto.Extensions[unittest_pb2.repeated_int32_extension ].append(5) + proto.Extensions[unittest_pb2.repeated_int32_extension ].append(11) + proto.Extensions[unittest_pb2.repeated_string_extension ].append('foo') + proto.Extensions[unittest_pb2.repeated_string_extension ].append('bar') + proto.Extensions[unittest_pb2.repeated_string_extension ].append('baz') + proto.Extensions[unittest_pb2.optional_int32_extension ] = 21 + self.assertEqual( + [ (unittest_pb2.optional_int32_extension , 21), + (unittest_pb2.repeated_int32_extension , [5, 11]), + (unittest_pb2.repeated_fixed32_extension, [1]), + (unittest_pb2.repeated_string_extension , ['foo', 'bar', 'baz']) ], + proto.ListFields()) + + def testListFieldsAndExtensions(self): + proto = unittest_pb2.TestFieldOrderings() + test_util.SetAllFieldsAndExtensions(proto) + unittest_pb2.my_extension_int + self.assertEqual( + [ (proto.DESCRIPTOR.fields_by_name['my_int' ], 1), + (unittest_pb2.my_extension_int , 23), + (proto.DESCRIPTOR.fields_by_name['my_string'], 'foo'), + (unittest_pb2.my_extension_string , 'bar'), + (proto.DESCRIPTOR.fields_by_name['my_float' ], 1.0) ], + proto.ListFields()) + + def testDefaultValues(self): + proto = unittest_pb2.TestAllTypes() + self.assertEqual(0, proto.optional_int32) + self.assertEqual(0, proto.optional_int64) + self.assertEqual(0, proto.optional_uint32) + self.assertEqual(0, proto.optional_uint64) + self.assertEqual(0, proto.optional_sint32) + self.assertEqual(0, proto.optional_sint64) + self.assertEqual(0, proto.optional_fixed32) + self.assertEqual(0, proto.optional_fixed64) + self.assertEqual(0, proto.optional_sfixed32) + self.assertEqual(0, proto.optional_sfixed64) + self.assertEqual(0.0, proto.optional_float) + self.assertEqual(0.0, proto.optional_double) + self.assertEqual(False, proto.optional_bool) + self.assertEqual('', proto.optional_string) + self.assertEqual('', proto.optional_bytes) + + self.assertEqual(41, proto.default_int32) + self.assertEqual(42, proto.default_int64) + self.assertEqual(43, proto.default_uint32) + self.assertEqual(44, proto.default_uint64) + self.assertEqual(-45, proto.default_sint32) + self.assertEqual(46, proto.default_sint64) + self.assertEqual(47, proto.default_fixed32) + self.assertEqual(48, proto.default_fixed64) + self.assertEqual(49, proto.default_sfixed32) + self.assertEqual(-50, proto.default_sfixed64) + self.assertEqual(51.5, proto.default_float) + self.assertEqual(52e3, proto.default_double) + self.assertEqual(True, proto.default_bool) + self.assertEqual('hello', proto.default_string) + self.assertEqual('world', proto.default_bytes) + self.assertEqual(unittest_pb2.TestAllTypes.BAR, proto.default_nested_enum) + self.assertEqual(unittest_pb2.FOREIGN_BAR, proto.default_foreign_enum) + self.assertEqual(unittest_import_pb2.IMPORT_BAR, + proto.default_import_enum) + + def testHasFieldWithUnknownFieldName(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(ValueError, proto.HasField, 'nonexistent_field') + + def testClearFieldWithUnknownFieldName(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(ValueError, proto.ClearField, 'nonexistent_field') + + def testDisallowedAssignments(self): + # It's illegal to assign values directly to repeated fields + # or to nonrepeated composite fields. Ensure that this fails. + proto = unittest_pb2.TestAllTypes() + # Repeated fields. + self.assertRaises(AttributeError, setattr, proto, 'repeated_int32', 10) + # Lists shouldn't work, either. + self.assertRaises(AttributeError, setattr, proto, 'repeated_int32', [10]) + # Composite fields. + self.assertRaises(AttributeError, setattr, proto, + 'optional_nested_message', 23) + # proto.nonexistent_field = 23 should fail as well. + self.assertRaises(AttributeError, setattr, proto, 'nonexistent_field', 23) + + # TODO(robinson): Add type-safety check for enums. + def testSingleScalarTypeSafety(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(TypeError, setattr, proto, 'optional_int32', 1.1) + self.assertRaises(TypeError, setattr, proto, 'optional_int32', 'foo') + self.assertRaises(TypeError, setattr, proto, 'optional_string', 10) + self.assertRaises(TypeError, setattr, proto, 'optional_bytes', 10) + + def testSingleScalarBoundsChecking(self): + def TestMinAndMaxIntegers(field_name, expected_min, expected_max): + pb = unittest_pb2.TestAllTypes() + setattr(pb, field_name, expected_min) + setattr(pb, field_name, expected_max) + self.assertRaises(ValueError, setattr, pb, field_name, expected_min - 1) + self.assertRaises(ValueError, setattr, pb, field_name, expected_max + 1) + + TestMinAndMaxIntegers('optional_int32', -(1 << 31), (1 << 31) - 1) + TestMinAndMaxIntegers('optional_uint32', 0, 0xffffffff) + TestMinAndMaxIntegers('optional_int64', -(1 << 63), (1 << 63) - 1) + TestMinAndMaxIntegers('optional_uint64', 0, 0xffffffffffffffff) + TestMinAndMaxIntegers('optional_nested_enum', -(1 << 31), (1 << 31) - 1) + + def testRepeatedScalarTypeSafety(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(TypeError, proto.repeated_int32.append, 1.1) + self.assertRaises(TypeError, proto.repeated_int32.append, 'foo') + self.assertRaises(TypeError, proto.repeated_string, 10) + self.assertRaises(TypeError, proto.repeated_bytes, 10) + + proto.repeated_int32.append(10) + proto.repeated_int32[0] = 23 + self.assertRaises(IndexError, proto.repeated_int32.__setitem__, 500, 23) + self.assertRaises(TypeError, proto.repeated_int32.__setitem__, 0, 'abc') + + def testSingleScalarGettersAndSetters(self): + proto = unittest_pb2.TestAllTypes() + self.assertEqual(0, proto.optional_int32) + proto.optional_int32 = 1 + self.assertEqual(1, proto.optional_int32) + # TODO(robinson): Test all other scalar field types. + + def testSingleScalarClearField(self): + proto = unittest_pb2.TestAllTypes() + # Should be allowed to clear something that's not there (a no-op). + proto.ClearField('optional_int32') + proto.optional_int32 = 1 + self.assertTrue(proto.HasField('optional_int32')) + proto.ClearField('optional_int32') + self.assertEqual(0, proto.optional_int32) + self.assertTrue(not proto.HasField('optional_int32')) + # TODO(robinson): Test all other scalar field types. + + def testEnums(self): + proto = unittest_pb2.TestAllTypes() + self.assertEqual(1, proto.FOO) + self.assertEqual(1, unittest_pb2.TestAllTypes.FOO) + self.assertEqual(2, proto.BAR) + self.assertEqual(2, unittest_pb2.TestAllTypes.BAR) + self.assertEqual(3, proto.BAZ) + self.assertEqual(3, unittest_pb2.TestAllTypes.BAZ) + + def testRepeatedScalars(self): + proto = unittest_pb2.TestAllTypes() + + self.assertTrue(not proto.repeated_int32) + self.assertEqual(0, len(proto.repeated_int32)) + proto.repeated_int32.append(5); + proto.repeated_int32.append(10); + self.assertTrue(proto.repeated_int32) + self.assertEqual(2, len(proto.repeated_int32)) + + self.assertEqual([5, 10], proto.repeated_int32) + self.assertEqual(5, proto.repeated_int32[0]) + self.assertEqual(10, proto.repeated_int32[-1]) + # Test out-of-bounds indices. + self.assertRaises(IndexError, proto.repeated_int32.__getitem__, 1234) + self.assertRaises(IndexError, proto.repeated_int32.__getitem__, -1234) + # Test incorrect types passed to __getitem__. + self.assertRaises(TypeError, proto.repeated_int32.__getitem__, 'foo') + self.assertRaises(TypeError, proto.repeated_int32.__getitem__, None) + + # Test that we can use the field as an iterator. + result = [] + for i in proto.repeated_int32: + result.append(i) + self.assertEqual([5, 10], result) + + # Test clearing. + proto.ClearField('repeated_int32') + self.assertTrue(not proto.repeated_int32) + self.assertEqual(0, len(proto.repeated_int32)) + + def testRepeatedComposites(self): + proto = unittest_pb2.TestAllTypes() + self.assertTrue(not proto.repeated_nested_message) + self.assertEqual(0, len(proto.repeated_nested_message)) + m0 = proto.repeated_nested_message.add() + m1 = proto.repeated_nested_message.add() + self.assertTrue(proto.repeated_nested_message) + self.assertEqual(2, len(proto.repeated_nested_message)) + self.assertTrue(m0 is proto.repeated_nested_message[0]) + self.assertTrue(m1 is proto.repeated_nested_message[1]) + self.assertTrue(isinstance(m0, unittest_pb2.TestAllTypes.NestedMessage)) + + # Test out-of-bounds indices. + self.assertRaises(IndexError, proto.repeated_nested_message.__getitem__, + 1234) + self.assertRaises(IndexError, proto.repeated_nested_message.__getitem__, + -1234) + + # Test incorrect types passed to __getitem__. + self.assertRaises(TypeError, proto.repeated_nested_message.__getitem__, + 'foo') + self.assertRaises(TypeError, proto.repeated_nested_message.__getitem__, + None) + + # Test that we can use the field as an iterator. + result = [] + for i in proto.repeated_nested_message: + result.append(i) + self.assertEqual(2, len(result)) + self.assertTrue(m0 is result[0]) + self.assertTrue(m1 is result[1]) + + # Test clearing. + proto.ClearField('repeated_nested_message') + self.assertTrue(not proto.repeated_nested_message) + self.assertEqual(0, len(proto.repeated_nested_message)) + + def testHandWrittenReflection(self): + # TODO(robinson): We probably need a better way to specify + # protocol types by hand. But then again, this isn't something + # we expect many people to do. Hmm. + FieldDescriptor = descriptor.FieldDescriptor + foo_field_descriptor = FieldDescriptor( + name='foo_field', full_name='MyProto.foo_field', + index=0, number=1, type=FieldDescriptor.TYPE_INT64, + cpp_type=FieldDescriptor.CPPTYPE_INT64, + label=FieldDescriptor.LABEL_OPTIONAL, default_value=0, + containing_type=None, message_type=None, enum_type=None, + is_extension=False, extension_scope=None, + options=descriptor_pb2.FieldOptions()) + mydescriptor = descriptor.Descriptor( + name='MyProto', full_name='MyProto', filename='ignored', + containing_type=None, nested_types=[], enum_types=[], + fields=[foo_field_descriptor], extensions=[], + options=descriptor_pb2.MessageOptions()) + class MyProtoClass(message.Message): + DESCRIPTOR = mydescriptor + __metaclass__ = reflection.GeneratedProtocolMessageType + myproto_instance = MyProtoClass() + self.assertEqual(0, myproto_instance.foo_field) + self.assertTrue(not myproto_instance.HasField('foo_field')) + myproto_instance.foo_field = 23 + self.assertEqual(23, myproto_instance.foo_field) + self.assertTrue(myproto_instance.HasField('foo_field')) + + def testTopLevelExtensionsForOptionalScalar(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.optional_int32_extension + self.assertTrue(not extendee_proto.HasExtension(extension)) + self.assertEqual(0, extendee_proto.Extensions[extension]) + # As with normal scalar fields, just doing a read doesn't actually set the + # "has" bit. + self.assertTrue(not extendee_proto.HasExtension(extension)) + # Actually set the thing. + extendee_proto.Extensions[extension] = 23 + self.assertEqual(23, extendee_proto.Extensions[extension]) + self.assertTrue(extendee_proto.HasExtension(extension)) + # Ensure that clearing works as well. + extendee_proto.ClearExtension(extension) + self.assertEqual(0, extendee_proto.Extensions[extension]) + self.assertTrue(not extendee_proto.HasExtension(extension)) + + def testTopLevelExtensionsForRepeatedScalar(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.repeated_string_extension + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + extendee_proto.Extensions[extension].append('foo') + self.assertEqual(['foo'], extendee_proto.Extensions[extension]) + string_list = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + self.assertTrue(string_list is not extendee_proto.Extensions[extension]) + # Shouldn't be allowed to do Extensions[extension] = 'a' + self.assertRaises(TypeError, operator.setitem, extendee_proto.Extensions, + extension, 'a') + + def testTopLevelExtensionsForOptionalMessage(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.optional_foreign_message_extension + self.assertTrue(not extendee_proto.HasExtension(extension)) + self.assertEqual(0, extendee_proto.Extensions[extension].c) + # As with normal (non-extension) fields, merely reading from the + # thing shouldn't set the "has" bit. + self.assertTrue(not extendee_proto.HasExtension(extension)) + extendee_proto.Extensions[extension].c = 23 + self.assertEqual(23, extendee_proto.Extensions[extension].c) + self.assertTrue(extendee_proto.HasExtension(extension)) + # Save a reference here. + foreign_message = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + self.assertTrue(foreign_message is not extendee_proto.Extensions[extension]) + # Setting a field on foreign_message now shouldn't set + # any "has" bits on extendee_proto. + foreign_message.c = 42 + self.assertEqual(42, foreign_message.c) + self.assertTrue(foreign_message.HasField('c')) + self.assertTrue(not extendee_proto.HasExtension(extension)) + # Shouldn't be allowed to do Extensions[extension] = 'a' + self.assertRaises(TypeError, operator.setitem, extendee_proto.Extensions, + extension, 'a') + + def testTopLevelExtensionsForRepeatedMessage(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.repeatedgroup_extension + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + group = extendee_proto.Extensions[extension].add() + group.a = 23 + self.assertEqual(23, extendee_proto.Extensions[extension][0].a) + group.a = 42 + self.assertEqual(42, extendee_proto.Extensions[extension][0].a) + group_list = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + self.assertTrue(group_list is not extendee_proto.Extensions[extension]) + # Shouldn't be allowed to do Extensions[extension] = 'a' + self.assertRaises(TypeError, operator.setitem, extendee_proto.Extensions, + extension, 'a') + + def testNestedExtensions(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.TestRequired.single + + # We just test the non-repeated case. + self.assertTrue(not extendee_proto.HasExtension(extension)) + required = extendee_proto.Extensions[extension] + self.assertEqual(0, required.a) + self.assertTrue(not extendee_proto.HasExtension(extension)) + required.a = 23 + self.assertEqual(23, extendee_proto.Extensions[extension].a) + self.assertTrue(extendee_proto.HasExtension(extension)) + extendee_proto.ClearExtension(extension) + self.assertTrue(required is not extendee_proto.Extensions[extension]) + self.assertTrue(not extendee_proto.HasExtension(extension)) + + # If message A directly contains message B, and + # a.HasField('b') is currently False, then mutating any + # extension in B should change a.HasField('b') to True + # (and so on up the object tree). + def testHasBitsForAncestorsOfExtendedMessage(self): + # Optional scalar extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual(0, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_int_extension]) + self.assertTrue(not toplevel.HasField('submessage')) + toplevel.submessage.Extensions[ + more_extensions_pb2.optional_int_extension] = 23 + self.assertEqual(23, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_int_extension]) + self.assertTrue(toplevel.HasField('submessage')) + + # Repeated scalar extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual([], toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_int_extension]) + self.assertTrue(not toplevel.HasField('submessage')) + toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_int_extension].append(23) + self.assertEqual([23], toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_int_extension]) + self.assertTrue(toplevel.HasField('submessage')) + + # Optional message extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual(0, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_message_extension].foreign_message_int) + self.assertTrue(not toplevel.HasField('submessage')) + toplevel.submessage.Extensions[ + more_extensions_pb2.optional_message_extension].foreign_message_int = 23 + self.assertEqual(23, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_message_extension].foreign_message_int) + self.assertTrue(toplevel.HasField('submessage')) + + # Repeated message extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual(0, len(toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_message_extension])) + self.assertTrue(not toplevel.HasField('submessage')) + foreign = toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_message_extension].add() + self.assertTrue(foreign is toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_message_extension][0]) + self.assertTrue(toplevel.HasField('submessage')) + + def testDisconnectionAfterClearingEmptyMessage(self): + toplevel = more_extensions_pb2.TopLevelMessage() + extendee_proto = toplevel.submessage + extension = more_extensions_pb2.optional_message_extension + extension_proto = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + extension_proto.foreign_message_int = 23 + + self.assertTrue(not toplevel.HasField('submessage')) + self.assertTrue(extension_proto is not extendee_proto.Extensions[extension]) + + def testExtensionFailureModes(self): + extendee_proto = unittest_pb2.TestAllExtensions() + + # Try non-extension-handle arguments to HasExtension, + # ClearExtension(), and Extensions[]... + self.assertRaises(KeyError, extendee_proto.HasExtension, 1234) + self.assertRaises(KeyError, extendee_proto.ClearExtension, 1234) + self.assertRaises(KeyError, extendee_proto.Extensions.__getitem__, 1234) + self.assertRaises(KeyError, extendee_proto.Extensions.__setitem__, 1234, 5) + + # Try something that *is* an extension handle, just not for + # this message... + unknown_handle = more_extensions_pb2.optional_int_extension + self.assertRaises(KeyError, extendee_proto.HasExtension, + unknown_handle) + self.assertRaises(KeyError, extendee_proto.ClearExtension, + unknown_handle) + self.assertRaises(KeyError, extendee_proto.Extensions.__getitem__, + unknown_handle) + self.assertRaises(KeyError, extendee_proto.Extensions.__setitem__, + unknown_handle, 5) + + # Try call HasExtension() with a valid handle, but for a + # *repeated* field. (Just as with non-extension repeated + # fields, Has*() isn't supported for extension repeated fields). + self.assertRaises(KeyError, extendee_proto.HasExtension, + unittest_pb2.repeated_string_extension) + + def testCopyFrom(self): + # TODO(robinson): Implement. + pass + + def testClear(self): + proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(proto) + # Clear the message. + proto.Clear() + self.assertEquals(proto.ByteSize(), 0) + empty_proto = unittest_pb2.TestAllTypes() + self.assertEquals(proto, empty_proto) + + # Test if extensions which were set are cleared. + proto = unittest_pb2.TestAllExtensions() + test_util.SetAllExtensions(proto) + # Clear the message. + proto.Clear() + self.assertEquals(proto.ByteSize(), 0) + empty_proto = unittest_pb2.TestAllExtensions() + self.assertEquals(proto, empty_proto) + + def testIsInitialized(self): + # Trivial cases - all optional fields and extensions. + proto = unittest_pb2.TestAllTypes() + self.assertTrue(proto.IsInitialized()) + proto = unittest_pb2.TestAllExtensions() + self.assertTrue(proto.IsInitialized()) + + # The case of uninitialized required fields. + proto = unittest_pb2.TestRequired() + self.assertFalse(proto.IsInitialized()) + proto.a = proto.b = proto.c = 2 + self.assertTrue(proto.IsInitialized()) + + # The case of uninitialized submessage. + proto = unittest_pb2.TestRequiredForeign() + self.assertTrue(proto.IsInitialized()) + proto.optional_message.a = 1 + self.assertFalse(proto.IsInitialized()) + proto.optional_message.b = 0 + proto.optional_message.c = 0 + self.assertTrue(proto.IsInitialized()) + + # Uninitialized repeated submessage. + message1 = proto.repeated_message.add() + self.assertFalse(proto.IsInitialized()) + message1.a = message1.b = message1.c = 0 + self.assertTrue(proto.IsInitialized()) + + # Uninitialized repeated group in an extension. + proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.TestRequired.multi + message1 = proto.Extensions[extension].add() + message2 = proto.Extensions[extension].add() + self.assertFalse(proto.IsInitialized()) + message1.a = 1 + message1.b = 1 + message1.c = 1 + self.assertFalse(proto.IsInitialized()) + message2.a = 2 + message2.b = 2 + message2.c = 2 + self.assertTrue(proto.IsInitialized()) + + # Uninitialized nonrepeated message in an extension. + proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.TestRequired.single + proto.Extensions[extension].a = 1 + self.assertFalse(proto.IsInitialized()) + proto.Extensions[extension].b = 2 + proto.Extensions[extension].c = 3 + self.assertTrue(proto.IsInitialized()) + + +# Since we had so many tests for protocol buffer equality, we broke these out +# into separate TestCase classes. + + +class TestAllTypesEqualityTest(unittest.TestCase): + + def setUp(self): + self.first_proto = unittest_pb2.TestAllTypes() + self.second_proto = unittest_pb2.TestAllTypes() + + def testSelfEquality(self): + self.assertEqual(self.first_proto, self.first_proto) + + def testEmptyProtosEqual(self): + self.assertEqual(self.first_proto, self.second_proto) + + +class FullProtosEqualityTest(unittest.TestCase): + + """Equality tests using completely-full protos as a starting point.""" + + def setUp(self): + self.first_proto = unittest_pb2.TestAllTypes() + self.second_proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(self.first_proto) + test_util.SetAllFields(self.second_proto) + + def testAllFieldsFilledEquality(self): + self.assertEqual(self.first_proto, self.second_proto) + + def testNonRepeatedScalar(self): + # Nonrepeated scalar field change should cause inequality. + self.first_proto.optional_int32 += 1 + self.assertNotEqual(self.first_proto, self.second_proto) + # ...as should clearing a field. + self.first_proto.ClearField('optional_int32') + self.assertNotEqual(self.first_proto, self.second_proto) + + def testNonRepeatedComposite(self): + # Change a nonrepeated composite field. + self.first_proto.optional_nested_message.bb += 1 + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.optional_nested_message.bb -= 1 + self.assertEqual(self.first_proto, self.second_proto) + # Clear a field in the nested message. + self.first_proto.optional_nested_message.ClearField('bb') + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.optional_nested_message.bb = ( + self.second_proto.optional_nested_message.bb) + self.assertEqual(self.first_proto, self.second_proto) + # Remove the nested message entirely. + self.first_proto.ClearField('optional_nested_message') + self.assertNotEqual(self.first_proto, self.second_proto) + + def testRepeatedScalar(self): + # Change a repeated scalar field. + self.first_proto.repeated_int32.append(5) + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.ClearField('repeated_int32') + self.assertNotEqual(self.first_proto, self.second_proto) + + def testRepeatedComposite(self): + # Change value within a repeated composite field. + self.first_proto.repeated_nested_message[0].bb += 1 + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.repeated_nested_message[0].bb -= 1 + self.assertEqual(self.first_proto, self.second_proto) + # Add a value to a repeated composite field. + self.first_proto.repeated_nested_message.add() + self.assertNotEqual(self.first_proto, self.second_proto) + self.second_proto.repeated_nested_message.add() + self.assertEqual(self.first_proto, self.second_proto) + + def testNonRepeatedScalarHasBits(self): + # Ensure that we test "has" bits as well as value for + # nonrepeated scalar field. + self.first_proto.ClearField('optional_int32') + self.second_proto.optional_int32 = 0 + self.assertNotEqual(self.first_proto, self.second_proto) + + def testNonRepeatedCompositeHasBits(self): + # Ensure that we test "has" bits as well as value for + # nonrepeated composite field. + self.first_proto.ClearField('optional_nested_message') + self.second_proto.optional_nested_message.ClearField('bb') + self.assertNotEqual(self.first_proto, self.second_proto) + # TODO(robinson): Replace next two lines with method + # to set the "has" bit without changing the value, + # if/when such a method exists. + self.first_proto.optional_nested_message.bb = 0 + self.first_proto.optional_nested_message.ClearField('bb') + self.assertEqual(self.first_proto, self.second_proto) + + +class ExtensionEqualityTest(unittest.TestCase): + + def testExtensionEquality(self): + first_proto = unittest_pb2.TestAllExtensions() + second_proto = unittest_pb2.TestAllExtensions() + self.assertEqual(first_proto, second_proto) + test_util.SetAllExtensions(first_proto) + self.assertNotEqual(first_proto, second_proto) + test_util.SetAllExtensions(second_proto) + self.assertEqual(first_proto, second_proto) + + # Ensure that we check value equality. + first_proto.Extensions[unittest_pb2.optional_int32_extension] += 1 + self.assertNotEqual(first_proto, second_proto) + first_proto.Extensions[unittest_pb2.optional_int32_extension] -= 1 + self.assertEqual(first_proto, second_proto) + + # Ensure that we also look at "has" bits. + first_proto.ClearExtension(unittest_pb2.optional_int32_extension) + second_proto.Extensions[unittest_pb2.optional_int32_extension] = 0 + self.assertNotEqual(first_proto, second_proto) + first_proto.Extensions[unittest_pb2.optional_int32_extension] = 0 + self.assertEqual(first_proto, second_proto) + + # Ensure that differences in cached values + # don't matter if "has" bits are both false. + first_proto = unittest_pb2.TestAllExtensions() + second_proto = unittest_pb2.TestAllExtensions() + self.assertEqual( + 0, first_proto.Extensions[unittest_pb2.optional_int32_extension]) + self.assertEqual(first_proto, second_proto) + + +class MutualRecursionEqualityTest(unittest.TestCase): + + def testEqualityWithMutualRecursion(self): + first_proto = unittest_pb2.TestMutualRecursionA() + second_proto = unittest_pb2.TestMutualRecursionA() + self.assertEqual(first_proto, second_proto) + first_proto.bb.a.bb.optional_int32 = 23 + self.assertNotEqual(first_proto, second_proto) + second_proto.bb.a.bb.optional_int32 = 23 + self.assertEqual(first_proto, second_proto) + + +class ByteSizeTest(unittest.TestCase): + + def setUp(self): + self.proto = unittest_pb2.TestAllTypes() + self.extended_proto = more_extensions_pb2.ExtendedMessage() + + def Size(self): + return self.proto.ByteSize() + + def testEmptyMessage(self): + self.assertEqual(0, self.proto.ByteSize()) + + def testVarints(self): + def Test(i, expected_varint_size): + self.proto.Clear() + self.proto.optional_int64 = i + # Add one to the varint size for the tag info + # for tag 1. + self.assertEqual(expected_varint_size + 1, self.Size()) + Test(0, 1) + Test(1, 1) + for i, num_bytes in zip(range(7, 63, 7), range(1, 10000)): + Test((1 << i) - 1, num_bytes) + Test(-1, 10) + Test(-2, 10) + Test(-(1 << 63), 10) + + def testStrings(self): + self.proto.optional_string = '' + # Need one byte for tag info (tag #14), and one byte for length. + self.assertEqual(2, self.Size()) + + self.proto.optional_string = 'abc' + # Need one byte for tag info (tag #14), and one byte for length. + self.assertEqual(2 + len(self.proto.optional_string), self.Size()) + + self.proto.optional_string = 'x' * 128 + # Need one byte for tag info (tag #14), and TWO bytes for length. + self.assertEqual(3 + len(self.proto.optional_string), self.Size()) + + def testOtherNumerics(self): + self.proto.optional_fixed32 = 1234 + # One byte for tag and 4 bytes for fixed32. + self.assertEqual(5, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_fixed64 = 1234 + # One byte for tag and 8 bytes for fixed64. + self.assertEqual(9, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_float = 1.234 + # One byte for tag and 4 bytes for float. + self.assertEqual(5, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_double = 1.234 + # One byte for tag and 8 bytes for float. + self.assertEqual(9, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_sint32 = 64 + # One byte for tag and 2 bytes for zig-zag-encoded 64. + self.assertEqual(3, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + def testComposites(self): + # 3 bytes. + self.proto.optional_nested_message.bb = (1 << 14) + # Plus one byte for bb tag. + # Plus 1 byte for optional_nested_message serialized size. + # Plus two bytes for optional_nested_message tag. + self.assertEqual(3 + 1 + 1 + 2, self.Size()) + + def testGroups(self): + # 4 bytes. + self.proto.optionalgroup.a = (1 << 21) + # Plus two bytes for |a| tag. + # Plus 2 * two bytes for START_GROUP and END_GROUP tags. + self.assertEqual(4 + 2 + 2*2, self.Size()) + + def testRepeatedScalars(self): + self.proto.repeated_int32.append(10) # 1 byte. + self.proto.repeated_int32.append(128) # 2 bytes. + # Also need 2 bytes for each entry for tag. + self.assertEqual(1 + 2 + 2*2, self.Size()) + + def testRepeatedComposites(self): + # Empty message. 2 bytes tag plus 1 byte length. + foreign_message_0 = self.proto.repeated_nested_message.add() + # 2 bytes tag plus 1 byte length plus 1 byte bb tag 1 byte int. + foreign_message_1 = self.proto.repeated_nested_message.add() + foreign_message_1.bb = 7 + self.assertEqual(2 + 1 + 2 + 1 + 1 + 1, self.Size()) + + def testRepeatedGroups(self): + # 2-byte START_GROUP plus 2-byte END_GROUP. + group_0 = self.proto.repeatedgroup.add() + # 2-byte START_GROUP plus 2-byte |a| tag + 1-byte |a| + # plus 2-byte END_GROUP. + group_1 = self.proto.repeatedgroup.add() + group_1.a = 7 + self.assertEqual(2 + 2 + 2 + 2 + 1 + 2, self.Size()) + + def testExtensions(self): + proto = unittest_pb2.TestAllExtensions() + self.assertEqual(0, proto.ByteSize()) + extension = unittest_pb2.optional_int32_extension # Field #1, 1 byte. + proto.Extensions[extension] = 23 + # 1 byte for tag, 1 byte for value. + self.assertEqual(2, proto.ByteSize()) + + def testCacheInvalidationForNonrepeatedScalar(self): + # Test non-extension. + self.proto.optional_int32 = 1 + self.assertEqual(2, self.proto.ByteSize()) + self.proto.optional_int32 = 128 + self.assertEqual(3, self.proto.ByteSize()) + self.proto.ClearField('optional_int32') + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.optional_int_extension + self.extended_proto.Extensions[extension] = 1 + self.assertEqual(2, self.extended_proto.ByteSize()) + self.extended_proto.Extensions[extension] = 128 + self.assertEqual(3, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + def testCacheInvalidationForRepeatedScalar(self): + # Test non-extension. + self.proto.repeated_int32.append(1) + self.assertEqual(3, self.proto.ByteSize()) + self.proto.repeated_int32.append(1) + self.assertEqual(6, self.proto.ByteSize()) + self.proto.repeated_int32[1] = 128 + self.assertEqual(7, self.proto.ByteSize()) + self.proto.ClearField('repeated_int32') + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.repeated_int_extension + repeated = self.extended_proto.Extensions[extension] + repeated.append(1) + self.assertEqual(2, self.extended_proto.ByteSize()) + repeated.append(1) + self.assertEqual(4, self.extended_proto.ByteSize()) + repeated[1] = 128 + self.assertEqual(5, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + def testCacheInvalidationForNonrepeatedMessage(self): + # Test non-extension. + self.proto.optional_foreign_message.c = 1 + self.assertEqual(5, self.proto.ByteSize()) + self.proto.optional_foreign_message.c = 128 + self.assertEqual(6, self.proto.ByteSize()) + self.proto.optional_foreign_message.ClearField('c') + self.assertEqual(3, self.proto.ByteSize()) + self.proto.ClearField('optional_foreign_message') + self.assertEqual(0, self.proto.ByteSize()) + child = self.proto.optional_foreign_message + self.proto.ClearField('optional_foreign_message') + child.c = 128 + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.optional_message_extension + child = self.extended_proto.Extensions[extension] + self.assertEqual(0, self.extended_proto.ByteSize()) + child.foreign_message_int = 1 + self.assertEqual(4, self.extended_proto.ByteSize()) + child.foreign_message_int = 128 + self.assertEqual(5, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + def testCacheInvalidationForRepeatedMessage(self): + # Test non-extension. + child0 = self.proto.repeated_foreign_message.add() + self.assertEqual(3, self.proto.ByteSize()) + self.proto.repeated_foreign_message.add() + self.assertEqual(6, self.proto.ByteSize()) + child0.c = 1 + self.assertEqual(8, self.proto.ByteSize()) + self.proto.ClearField('repeated_foreign_message') + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.repeated_message_extension + child_list = self.extended_proto.Extensions[extension] + child0 = child_list.add() + self.assertEqual(2, self.extended_proto.ByteSize()) + child_list.add() + self.assertEqual(4, self.extended_proto.ByteSize()) + child0.foreign_message_int = 1 + self.assertEqual(6, self.extended_proto.ByteSize()) + child0.ClearField('foreign_message_int') + self.assertEqual(4, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + +# TODO(robinson): We need cross-language serialization consistency tests. +# Issues to be sure to cover include: +# * Handling of unrecognized tags ("uninterpreted_bytes"). +# * Handling of MessageSets. +# * Consistent ordering of tags in the wire format, +# including ordering between extensions and non-extension +# fields. +# * Consistent serialization of negative numbers, especially +# negative int32s. +# * Handling of empty submessages (with and without "has" +# bits set). + +class SerializationTest(unittest.TestCase): + + def testSerializeEmtpyMessage(self): + first_proto = unittest_pb2.TestAllTypes() + second_proto = unittest_pb2.TestAllTypes() + serialized = first_proto.SerializeToString() + self.assertEqual(first_proto.ByteSize(), len(serialized)) + second_proto.MergeFromString(serialized) + self.assertEqual(first_proto, second_proto) + + def testSerializeAllFields(self): + first_proto = unittest_pb2.TestAllTypes() + second_proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(first_proto) + serialized = first_proto.SerializeToString() + self.assertEqual(first_proto.ByteSize(), len(serialized)) + second_proto.MergeFromString(serialized) + self.assertEqual(first_proto, second_proto) + + def testSerializeAllExtensions(self): + first_proto = unittest_pb2.TestAllExtensions() + second_proto = unittest_pb2.TestAllExtensions() + test_util.SetAllExtensions(first_proto) + serialized = first_proto.SerializeToString() + second_proto.MergeFromString(serialized) + self.assertEqual(first_proto, second_proto) + + def testCanonicalSerializationOrder(self): + proto = more_messages_pb2.OutOfOrderFields() + # These are also their tag numbers. Even though we're setting these in + # reverse-tag order AND they're listed in reverse tag-order in the .proto + # file, they should nonetheless be serialized in tag order. + proto.optional_sint32 = 5 + proto.Extensions[more_messages_pb2.optional_uint64] = 4 + proto.optional_uint32 = 3 + proto.Extensions[more_messages_pb2.optional_int64] = 2 + proto.optional_int32 = 1 + serialized = proto.SerializeToString() + self.assertEqual(proto.ByteSize(), len(serialized)) + d = decoder.Decoder(serialized) + ReadTag = d.ReadFieldNumberAndWireType + self.assertEqual((1, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(1, d.ReadInt32()) + self.assertEqual((2, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(2, d.ReadInt64()) + self.assertEqual((3, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(3, d.ReadUInt32()) + self.assertEqual((4, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(4, d.ReadUInt64()) + self.assertEqual((5, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(5, d.ReadSInt32()) + + def testCanonicalSerializationOrderSameAsCpp(self): + # Copy of the same test we use for C++. + proto = unittest_pb2.TestFieldOrderings() + test_util.SetAllFieldsAndExtensions(proto) + serialized = proto.SerializeToString() + test_util.ExpectAllFieldsAndExtensionsInOrder(serialized) + + def testMergeFromStringWhenFieldsAlreadySet(self): + first_proto = unittest_pb2.TestAllTypes() + first_proto.repeated_string.append('foobar') + first_proto.optional_int32 = 23 + first_proto.optional_nested_message.bb = 42 + serialized = first_proto.SerializeToString() + + second_proto = unittest_pb2.TestAllTypes() + second_proto.repeated_string.append('baz') + second_proto.optional_int32 = 100 + second_proto.optional_nested_message.bb = 999 + + second_proto.MergeFromString(serialized) + # Ensure that we append to repeated fields. + self.assertEqual(['baz', 'foobar'], list(second_proto.repeated_string)) + # Ensure that we overwrite nonrepeatd scalars. + self.assertEqual(23, second_proto.optional_int32) + # Ensure that we recursively call MergeFromString() on + # submessages. + self.assertEqual(42, second_proto.optional_nested_message.bb) + + def testMessageSetWireFormat(self): + proto = unittest_mset_pb2.TestMessageSet() + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + extension_message2 = unittest_mset_pb2.TestMessageSetExtension2 + extension1 = extension_message1.message_set_extension + extension2 = extension_message2.message_set_extension + proto.Extensions[extension1].i = 123 + proto.Extensions[extension2].str = 'foo' + + # Serialize using the MessageSet wire format (this is specified in the + # .proto file). + serialized = proto.SerializeToString() + + raw = unittest_mset_pb2.RawMessageSet() + self.assertEqual(False, + raw.DESCRIPTOR.GetOptions().message_set_wire_format) + raw.MergeFromString(serialized) + self.assertEqual(2, len(raw.item)) + + message1 = unittest_mset_pb2.TestMessageSetExtension1() + message1.MergeFromString(raw.item[0].message) + self.assertEqual(123, message1.i) + + message2 = unittest_mset_pb2.TestMessageSetExtension2() + message2.MergeFromString(raw.item[1].message) + self.assertEqual('foo', message2.str) + + # Deserialize using the MessageSet wire format. + proto2 = unittest_mset_pb2.TestMessageSet() + proto2.MergeFromString(serialized) + self.assertEqual(123, proto2.Extensions[extension1].i) + self.assertEqual('foo', proto2.Extensions[extension2].str) + + # Check byte size. + self.assertEqual(proto2.ByteSize(), len(serialized)) + self.assertEqual(proto.ByteSize(), len(serialized)) + + def testMessageSetWireFormatUnknownExtension(self): + # Create a message using the message set wire format with an unknown + # message. + raw = unittest_mset_pb2.RawMessageSet() + + # Add an item. + item = raw.item.add() + item.type_id = 1545008 + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + message1 = unittest_mset_pb2.TestMessageSetExtension1() + message1.i = 12345 + item.message = message1.SerializeToString() + + # Add a second, unknown extension. + item = raw.item.add() + item.type_id = 1545009 + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + message1 = unittest_mset_pb2.TestMessageSetExtension1() + message1.i = 12346 + item.message = message1.SerializeToString() + + # Add another unknown extension. + item = raw.item.add() + item.type_id = 1545010 + message1 = unittest_mset_pb2.TestMessageSetExtension2() + message1.str = 'foo' + item.message = message1.SerializeToString() + + serialized = raw.SerializeToString() + + # Parse message using the message set wire format. + proto = unittest_mset_pb2.TestMessageSet() + proto.MergeFromString(serialized) + + # Check that the message parsed well. + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + extension1 = extension_message1.message_set_extension + self.assertEquals(12345, proto.Extensions[extension1].i) + + def testUnknownFields(self): + proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(proto) + + serialized = proto.SerializeToString() + + # The empty message should be parsable with all of the fields + # unknown. + proto2 = unittest_pb2.TestEmptyMessage() + + # Parsing this message should succeed. + proto2.MergeFromString(serialized) + + +class OptionsTest(unittest.TestCase): + + def testMessageOptions(self): + proto = unittest_mset_pb2.TestMessageSet() + self.assertEqual(True, + proto.DESCRIPTOR.GetOptions().message_set_wire_format) + proto = unittest_pb2.TestAllTypes() + self.assertEqual(False, + proto.DESCRIPTOR.GetOptions().message_set_wire_format) + + +class UtilityTest(unittest.TestCase): + + def testImergeSorted(self): + ImergeSorted = reflection._ImergeSorted + # Various types of emptiness. + self.assertEqual([], list(ImergeSorted())) + self.assertEqual([], list(ImergeSorted([]))) + self.assertEqual([], list(ImergeSorted([], []))) + + # One nonempty list. + self.assertEqual([1, 2, 3], list(ImergeSorted([1, 2, 3]))) + self.assertEqual([1, 2, 3], list(ImergeSorted([1, 2, 3], []))) + self.assertEqual([1, 2, 3], list(ImergeSorted([], [1, 2, 3]))) + + # Merging some nonempty lists together. + self.assertEqual([1, 2, 3], list(ImergeSorted([1, 3], [2]))) + self.assertEqual([1, 2, 3], list(ImergeSorted([1], [3], [2]))) + self.assertEqual([1, 2, 3], list(ImergeSorted([1], [3], [2], []))) + + # Elements repeated across component iterators. + self.assertEqual([1, 2, 2, 3, 3], + list(ImergeSorted([1, 2], [3], [2, 3]))) + + # Elements repeated within an iterator. + self.assertEqual([1, 2, 2, 3, 3], + list(ImergeSorted([1, 2, 2], [3], [3]))) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/service_reflection_test.py b/python/google/protobuf/internal/service_reflection_test.py new file mode 100755 index 00000000..895d24d3 --- /dev/null +++ b/python/google/protobuf/internal/service_reflection_test.py @@ -0,0 +1,98 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for google.protobuf.internal.service_reflection.""" + +__author__ = 'petar@google.com (Petar Petrov)' + +import unittest +from google.protobuf import unittest_pb2 +from google.protobuf import service_reflection +from google.protobuf import service + + +class FooUnitTest(unittest.TestCase): + + def testService(self): + class MockRpcChannel(service.RpcChannel): + def CallMethod(self, method, controller, request, response, callback): + self.method = method + self.controller = controller + self.request = request + callback(response) + + class MockRpcController(service.RpcController): + def SetFailed(self, msg): + self.failure_message = msg + + self.callback_response = None + + class MyService(unittest_pb2.TestService): + pass + + self.callback_response = None + + def MyCallback(response): + self.callback_response = response + + rpc_controller = MockRpcController() + channel = MockRpcChannel() + srvc = MyService() + srvc.Foo(rpc_controller, unittest_pb2.FooRequest(), MyCallback) + self.assertEqual('Method Foo not implemented.', + rpc_controller.failure_message) + self.assertEqual(None, self.callback_response) + + rpc_controller.failure_message = None + + service_descriptor = unittest_pb2.TestService.DESCRIPTOR + srvc.CallMethod(service_descriptor.methods[1], rpc_controller, + unittest_pb2.BarRequest(), MyCallback) + self.assertEqual('Method Bar not implemented.', + rpc_controller.failure_message) + self.assertEqual(None, self.callback_response) + + def testServiceStub(self): + class MockRpcChannel(service.RpcChannel): + def CallMethod(self, method, controller, request, + response_class, callback): + self.method = method + self.controller = controller + self.request = request + callback(response_class()) + + self.callback_response = None + + def MyCallback(response): + self.callback_response = response + + channel = MockRpcChannel() + stub = unittest_pb2.TestService_Stub(channel) + rpc_controller = 'controller' + request = 'request' + + # Invoke method. + stub.Foo(rpc_controller, request, MyCallback) + + self.assertTrue(isinstance(self.callback_response, + unittest_pb2.FooResponse)) + self.assertEqual(request, channel.request) + self.assertEqual(rpc_controller, channel.controller) + self.assertEqual(stub.GetDescriptor().methods[0], channel.method) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/test_util.py b/python/google/protobuf/internal/test_util.py new file mode 100755 index 00000000..d9106421 --- /dev/null +++ b/python/google/protobuf/internal/test_util.py @@ -0,0 +1,354 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities for Python proto2 tests. + +This is intentionally modeled on C++ code in +//net/proto2/internal/test_util.*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import os.path + +from google.protobuf import unittest_import_pb2 +from google.protobuf import unittest_pb2 + + +def SetAllFields(message): + """Sets every field in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllTypes instance. + """ + + # + # Optional fields. + # + + message.optional_int32 = 101 + message.optional_int64 = 102 + message.optional_uint32 = 103 + message.optional_uint64 = 104 + message.optional_sint32 = 105 + message.optional_sint64 = 106 + message.optional_fixed32 = 107 + message.optional_fixed64 = 108 + message.optional_sfixed32 = 109 + message.optional_sfixed64 = 110 + message.optional_float = 111 + message.optional_double = 112 + message.optional_bool = True + # TODO(robinson): Firmly spec out and test how + # protos interact with unicode. One specific example: + # what happens if we change the literal below to + # u'115'? What *should* happen? Still some discussion + # to finish with Kenton about bytes vs. strings + # and forcing everything to be utf8. :-/ + message.optional_string = '115' + message.optional_bytes = '116' + + message.optionalgroup.a = 117 + message.optional_nested_message.bb = 118 + message.optional_foreign_message.c = 119 + message.optional_import_message.d = 120 + + message.optional_nested_enum = unittest_pb2.TestAllTypes.BAZ + message.optional_foreign_enum = unittest_pb2.FOREIGN_BAZ + message.optional_import_enum = unittest_import_pb2.IMPORT_BAZ + + message.optional_string_piece = '124' + message.optional_cord = '125' + + # + # Repeated fields. + # + + message.repeated_int32.append(201) + message.repeated_int64.append(202) + message.repeated_uint32.append(203) + message.repeated_uint64.append(204) + message.repeated_sint32.append(205) + message.repeated_sint64.append(206) + message.repeated_fixed32.append(207) + message.repeated_fixed64.append(208) + message.repeated_sfixed32.append(209) + message.repeated_sfixed64.append(210) + message.repeated_float.append(211) + message.repeated_double.append(212) + message.repeated_bool.append(True) + message.repeated_string.append('215') + message.repeated_bytes.append('216') + + message.repeatedgroup.add().a = 217 + message.repeated_nested_message.add().bb = 218 + message.repeated_foreign_message.add().c = 219 + message.repeated_import_message.add().d = 220 + + message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAR) + message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAR) + message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAR) + + message.repeated_string_piece.append('224') + message.repeated_cord.append('225') + + # Add a second one of each field. + message.repeated_int32.append(301) + message.repeated_int64.append(302) + message.repeated_uint32.append(303) + message.repeated_uint64.append(304) + message.repeated_sint32.append(305) + message.repeated_sint64.append(306) + message.repeated_fixed32.append(307) + message.repeated_fixed64.append(308) + message.repeated_sfixed32.append(309) + message.repeated_sfixed64.append(310) + message.repeated_float.append(311) + message.repeated_double.append(312) + message.repeated_bool.append(False) + message.repeated_string.append('315') + message.repeated_bytes.append('316') + + message.repeatedgroup.add().a = 317 + message.repeated_nested_message.add().bb = 318 + message.repeated_foreign_message.add().c = 319 + message.repeated_import_message.add().d = 320 + + message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAZ) + message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAZ) + message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAZ) + + message.repeated_string_piece.append('324') + message.repeated_cord.append('325') + + # + # Fields that have defaults. + # + + message.default_int32 = 401 + message.default_int64 = 402 + message.default_uint32 = 403 + message.default_uint64 = 404 + message.default_sint32 = 405 + message.default_sint64 = 406 + message.default_fixed32 = 407 + message.default_fixed64 = 408 + message.default_sfixed32 = 409 + message.default_sfixed64 = 410 + message.default_float = 411 + message.default_double = 412 + message.default_bool = False + message.default_string = '415' + message.default_bytes = '416' + + message.default_nested_enum = unittest_pb2.TestAllTypes.FOO + message.default_foreign_enum = unittest_pb2.FOREIGN_FOO + message.default_import_enum = unittest_import_pb2.IMPORT_FOO + + message.default_string_piece = '424' + message.default_cord = '425' + + +def SetAllExtensions(message): + """Sets every extension in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllExtensions instance. + """ + + extensions = message.Extensions + pb2 = unittest_pb2 + import_pb2 = unittest_import_pb2 + + # + # Optional fields. + # + + extensions[pb2.optional_int32_extension] = 101 + extensions[pb2.optional_int64_extension] = 102 + extensions[pb2.optional_uint32_extension] = 103 + extensions[pb2.optional_uint64_extension] = 104 + extensions[pb2.optional_sint32_extension] = 105 + extensions[pb2.optional_sint64_extension] = 106 + extensions[pb2.optional_fixed32_extension] = 107 + extensions[pb2.optional_fixed64_extension] = 108 + extensions[pb2.optional_sfixed32_extension] = 109 + extensions[pb2.optional_sfixed64_extension] = 110 + extensions[pb2.optional_float_extension] = 111 + extensions[pb2.optional_double_extension] = 112 + extensions[pb2.optional_bool_extension] = True + extensions[pb2.optional_string_extension] = '115' + extensions[pb2.optional_bytes_extension] = '116' + + extensions[pb2.optionalgroup_extension].a = 117 + extensions[pb2.optional_nested_message_extension].bb = 118 + extensions[pb2.optional_foreign_message_extension].c = 119 + extensions[pb2.optional_import_message_extension].d = 120 + + extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ + extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ + extensions[pb2.optional_foreign_enum_extension] = pb2.FOREIGN_BAZ + extensions[pb2.optional_import_enum_extension] = import_pb2.IMPORT_BAZ + + extensions[pb2.optional_string_piece_extension] = '124' + extensions[pb2.optional_cord_extension] = '125' + + # + # Repeated fields. + # + + extensions[pb2.repeated_int32_extension].append(201) + extensions[pb2.repeated_int64_extension].append(202) + extensions[pb2.repeated_uint32_extension].append(203) + extensions[pb2.repeated_uint64_extension].append(204) + extensions[pb2.repeated_sint32_extension].append(205) + extensions[pb2.repeated_sint64_extension].append(206) + extensions[pb2.repeated_fixed32_extension].append(207) + extensions[pb2.repeated_fixed64_extension].append(208) + extensions[pb2.repeated_sfixed32_extension].append(209) + extensions[pb2.repeated_sfixed64_extension].append(210) + extensions[pb2.repeated_float_extension].append(211) + extensions[pb2.repeated_double_extension].append(212) + extensions[pb2.repeated_bool_extension].append(True) + extensions[pb2.repeated_string_extension].append('215') + extensions[pb2.repeated_bytes_extension].append('216') + + extensions[pb2.repeatedgroup_extension].add().a = 217 + extensions[pb2.repeated_nested_message_extension].add().bb = 218 + extensions[pb2.repeated_foreign_message_extension].add().c = 219 + extensions[pb2.repeated_import_message_extension].add().d = 220 + + extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAR) + extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAR) + extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAR) + + extensions[pb2.repeated_string_piece_extension].append('224') + extensions[pb2.repeated_cord_extension].append('225') + + # Append a second one of each field. + extensions[pb2.repeated_int32_extension].append(301) + extensions[pb2.repeated_int64_extension].append(302) + extensions[pb2.repeated_uint32_extension].append(303) + extensions[pb2.repeated_uint64_extension].append(304) + extensions[pb2.repeated_sint32_extension].append(305) + extensions[pb2.repeated_sint64_extension].append(306) + extensions[pb2.repeated_fixed32_extension].append(307) + extensions[pb2.repeated_fixed64_extension].append(308) + extensions[pb2.repeated_sfixed32_extension].append(309) + extensions[pb2.repeated_sfixed64_extension].append(310) + extensions[pb2.repeated_float_extension].append(311) + extensions[pb2.repeated_double_extension].append(312) + extensions[pb2.repeated_bool_extension].append(False) + extensions[pb2.repeated_string_extension].append('315') + extensions[pb2.repeated_bytes_extension].append('316') + + extensions[pb2.repeatedgroup_extension].add().a = 317 + extensions[pb2.repeated_nested_message_extension].add().bb = 318 + extensions[pb2.repeated_foreign_message_extension].add().c = 319 + extensions[pb2.repeated_import_message_extension].add().d = 320 + + extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAZ) + extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAZ) + extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAZ) + + extensions[pb2.repeated_string_piece_extension].append('324') + extensions[pb2.repeated_cord_extension].append('325') + + # + # Fields with defaults. + # + + extensions[pb2.default_int32_extension] = 401 + extensions[pb2.default_int64_extension] = 402 + extensions[pb2.default_uint32_extension] = 403 + extensions[pb2.default_uint64_extension] = 404 + extensions[pb2.default_sint32_extension] = 405 + extensions[pb2.default_sint64_extension] = 406 + extensions[pb2.default_fixed32_extension] = 407 + extensions[pb2.default_fixed64_extension] = 408 + extensions[pb2.default_sfixed32_extension] = 409 + extensions[pb2.default_sfixed64_extension] = 410 + extensions[pb2.default_float_extension] = 411 + extensions[pb2.default_double_extension] = 412 + extensions[pb2.default_bool_extension] = False + extensions[pb2.default_string_extension] = '415' + extensions[pb2.default_bytes_extension] = '416' + + extensions[pb2.default_nested_enum_extension] = pb2.TestAllTypes.FOO + extensions[pb2.default_foreign_enum_extension] = pb2.FOREIGN_FOO + extensions[pb2.default_import_enum_extension] = import_pb2.IMPORT_FOO + + extensions[pb2.default_string_piece_extension] = '424' + extensions[pb2.default_cord_extension] = '425' + + +def SetAllFieldsAndExtensions(message): + """Sets every field and extension in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllExtensions message. + """ + message.my_int = 1 + message.my_string = 'foo' + message.my_float = 1.0 + message.Extensions[unittest_pb2.my_extension_int] = 23 + message.Extensions[unittest_pb2.my_extension_string] = 'bar' + + +def ExpectAllFieldsAndExtensionsInOrder(serialized): + """Ensures that serialized is the serialization we expect for a message + filled with SetAllFieldsAndExtensions(). (Specifically, ensures that the + serialization is in canonical, tag-number order). + """ + my_extension_int = unittest_pb2.my_extension_int + my_extension_string = unittest_pb2.my_extension_string + expected_strings = [] + message = unittest_pb2.TestFieldOrderings() + message.my_int = 1 # Field 1. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.Extensions[my_extension_int] = 23 # Field 5. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.my_string = 'foo' # Field 11. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.Extensions[my_extension_string] = 'bar' # Field 50. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.my_float = 1.0 + expected_strings.append(message.SerializeToString()) + message.Clear() + expected = ''.join(expected_strings) + + if expected != serialized: + raise ValueError('Expected %r, found %r' % (expected, serialized)) + +def GoldenFile(filename): + """Finds the given golden file and returns a file object representing it.""" + + # Search up the directory tree looking for the C++ protobuf source code. + path = '.' + while os.path.exists(path): + if os.path.exists(os.path.join(path, 'src/google/protobuf')): + # Found it. Load the golden file from the testdata directory. + return file(os.path.join(path, 'src/google/protobuf/testdata', filename)) + path = os.path.join(path, '..') + + raise RuntimeError( + 'Could not find golden files. This test must be run from within the ' + 'protobuf source package so that it can read test data files from the ' + 'C++ source tree.') diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py new file mode 100755 index 00000000..c2074db5 --- /dev/null +++ b/python/google/protobuf/internal/text_format_test.py @@ -0,0 +1,97 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test for google.protobuf.text_format.""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +import difflib + +import unittest +from google.protobuf import text_format +from google.protobuf.internal import test_util +from google.protobuf import unittest_pb2 +from google.protobuf import unittest_mset_pb2 + +class TextFormatTest(unittest.TestCase): + def CompareToGoldenFile(self, text, golden_filename): + f = test_util.GoldenFile(golden_filename) + golden_lines = f.readlines() + f.close() + self.CompareToGoldenLines(text, golden_lines) + + def CompareToGoldenText(self, text, golden_text): + self.CompareToGoldenLines(text, golden_text.splitlines(1)) + + def CompareToGoldenLines(self, text, golden_lines): + actual_lines = text.splitlines(1) + self.assertEqual(golden_lines, actual_lines, + "Text doesn't match golden. Diff:\n" + + ''.join(difflib.ndiff(golden_lines, actual_lines))) + + def testPrintAllFields(self): + message = unittest_pb2.TestAllTypes() + test_util.SetAllFields(message) + self.CompareToGoldenFile(text_format.MessageToString(message), + 'text_format_unittest_data.txt') + + def testPrintAllExtensions(self): + message = unittest_pb2.TestAllExtensions() + test_util.SetAllExtensions(message) + self.CompareToGoldenFile(text_format.MessageToString(message), + 'text_format_unittest_extensions_data.txt') + + def testPrintMessageSet(self): + message = unittest_mset_pb2.TestMessageSetContainer() + ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension + ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension + message.message_set.Extensions[ext1].i = 23 + message.message_set.Extensions[ext2].str = 'foo' + self.CompareToGoldenText(text_format.MessageToString(message), + 'message_set {\n' + ' [protobuf_unittest.TestMessageSetExtension1] {\n' + ' i: 23\n' + ' }\n' + ' [protobuf_unittest.TestMessageSetExtension2] {\n' + ' str: \"foo\"\n' + ' }\n' + '}\n') + + def testPrintExotic(self): + message = unittest_pb2.TestAllTypes() + message.repeated_int64.append(-9223372036854775808); + message.repeated_uint64.append(18446744073709551615); + message.repeated_double.append(123.456); + message.repeated_double.append(1.23e22); + message.repeated_double.append(1.23e-18); + message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'\"'); + self.CompareToGoldenText(text_format.MessageToString(message), + 'repeated_int64: -9223372036854775808\n' + 'repeated_uint64: 18446744073709551615\n' + 'repeated_double: 123.456\n' + 'repeated_double: 1.23e+22\n' + 'repeated_double: 1.23e-18\n' + 'repeated_string: ' + '\"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\\"\"\n') + + def testMessageToString(self): + message = unittest_pb2.ForeignMessage() + message.c = 123 + self.assertEqual('c: 123\n', str(message)) + +if __name__ == '__main__': + unittest.main() + diff --git a/python/google/protobuf/internal/wire_format.py b/python/google/protobuf/internal/wire_format.py new file mode 100755 index 00000000..69aa4abf --- /dev/null +++ b/python/google/protobuf/internal/wire_format.py @@ -0,0 +1,222 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Constants and static functions to support protocol buffer wire format.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message + + +TAG_TYPE_BITS = 3 # Number of bits used to hold type info in a proto tag. +_TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1 # 0x7 + +# These numbers identify the wire type of a protocol buffer value. +# We use the least-significant TAG_TYPE_BITS bits of the varint-encoded +# tag-and-type to store one of these WIRETYPE_* constants. +# These values must match WireType enum in //net/proto2/public/wire_format.h. +WIRETYPE_VARINT = 0 +WIRETYPE_FIXED64 = 1 +WIRETYPE_LENGTH_DELIMITED = 2 +WIRETYPE_START_GROUP = 3 +WIRETYPE_END_GROUP = 4 +WIRETYPE_FIXED32 = 5 +_WIRETYPE_MAX = 5 + + +# Bounds for various integer types. +INT32_MAX = int((1 << 31) - 1) +INT32_MIN = int(-(1 << 31)) +UINT32_MAX = (1 << 32) - 1 + +INT64_MAX = (1 << 63) - 1 +INT64_MIN = -(1 << 63) +UINT64_MAX = (1 << 64) - 1 + +# "struct" format strings that will encode/decode the specified formats. +FORMAT_UINT32_LITTLE_ENDIAN = '> TAG_TYPE_BITS), (tag & _TAG_TYPE_MASK) + + +def ZigZagEncode(value): + """ZigZag Transform: Encodes signed integers so that they can be + effectively used with varint encoding. See wire_format.h for + more details. + """ + if value >= 0: + return value << 1 + return ((value << 1) ^ (~0)) | 0x1 + + +def ZigZagDecode(value): + """Inverse of ZigZagEncode().""" + if not value & 0x1: + return value >> 1 + return (value >> 1) ^ (~0) + + + +# The *ByteSize() functions below return the number of bytes required to +# serialize "field number + type" information and then serialize the value. + + +def Int32ByteSize(field_number, int32): + return Int64ByteSize(field_number, int32) + + +def Int64ByteSize(field_number, int64): + # Have to convert to uint before calling UInt64ByteSize(). + return UInt64ByteSize(field_number, 0xffffffffffffffff & int64) + + +def UInt32ByteSize(field_number, uint32): + return UInt64ByteSize(field_number, uint32) + + +def UInt64ByteSize(field_number, uint64): + return _TagByteSize(field_number) + _VarUInt64ByteSizeNoTag(uint64) + + +def SInt32ByteSize(field_number, int32): + return UInt32ByteSize(field_number, ZigZagEncode(int32)) + + +def SInt64ByteSize(field_number, int64): + return UInt64ByteSize(field_number, ZigZagEncode(int64)) + + +def Fixed32ByteSize(field_number, fixed32): + return _TagByteSize(field_number) + 4 + + +def Fixed64ByteSize(field_number, fixed64): + return _TagByteSize(field_number) + 8 + + +def SFixed32ByteSize(field_number, sfixed32): + return _TagByteSize(field_number) + 4 + + +def SFixed64ByteSize(field_number, sfixed64): + return _TagByteSize(field_number) + 8 + + +def FloatByteSize(field_number, flt): + return _TagByteSize(field_number) + 4 + + +def DoubleByteSize(field_number, double): + return _TagByteSize(field_number) + 8 + + +def BoolByteSize(field_number, b): + return _TagByteSize(field_number) + 1 + + +def EnumByteSize(field_number, enum): + return UInt32ByteSize(field_number, enum) + + +def StringByteSize(field_number, string): + return (_TagByteSize(field_number) + + _VarUInt64ByteSizeNoTag(len(string)) + + len(string)) + + +def BytesByteSize(field_number, b): + return StringByteSize(field_number, b) + + +def GroupByteSize(field_number, message): + return (2 * _TagByteSize(field_number) # START and END group. + + message.ByteSize()) + + +def MessageByteSize(field_number, message): + return (_TagByteSize(field_number) + + _VarUInt64ByteSizeNoTag(message.ByteSize()) + + message.ByteSize()) + + +def MessageSetItemByteSize(field_number, msg): + # First compute the sizes of the tags. + # There are 2 tags for the beginning and ending of the repeated group, that + # is field number 1, one with field number 2 (type_id) and one with field + # number 3 (message). + total_size = (2 * _TagByteSize(1) + _TagByteSize(2) + _TagByteSize(3)) + + # Add the number of bytes for type_id. + total_size += _VarUInt64ByteSizeNoTag(field_number) + + message_size = msg.ByteSize() + + # The number of bytes for encoding the length of the message. + total_size += _VarUInt64ByteSizeNoTag(message_size) + + # The size of the message. + total_size += message_size + return total_size + + +# Private helper functions for the *ByteSize() functions above. + + +def _TagByteSize(field_number): + """Returns the bytes required to serialize a tag with this field number.""" + # Just pass in type 0, since the type won't affect the tag+type size. + return _VarUInt64ByteSizeNoTag(PackTag(field_number, 0)) + + +def _VarUInt64ByteSizeNoTag(uint64): + """Returns the bytes required to serialize a single varint. + uint64 must be unsigned. + """ + if uint64 > UINT64_MAX: + raise message.EncodeError('Value out of range: %d' % uint64) + bytes = 1 + while uint64 > 0x7f: + bytes += 1 + uint64 >>= 7 + return bytes diff --git a/python/google/protobuf/internal/wire_format_test.py b/python/google/protobuf/internal/wire_format_test.py new file mode 100755 index 00000000..87e0ddf5 --- /dev/null +++ b/python/google/protobuf/internal/wire_format_test.py @@ -0,0 +1,232 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test for google.protobuf.internal.wire_format.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import message +from google.protobuf.internal import wire_format + + +class WireFormatTest(unittest.TestCase): + + def testPackTag(self): + field_number = 0xabc + tag_type = 2 + self.assertEqual((field_number << 3) | tag_type, + wire_format.PackTag(field_number, tag_type)) + PackTag = wire_format.PackTag + # Number too high. + self.assertRaises(message.EncodeError, PackTag, field_number, 6) + # Number too low. + self.assertRaises(message.EncodeError, PackTag, field_number, -1) + + def testUnpackTag(self): + # Test field numbers that will require various varint sizes. + for expected_field_number in (1, 15, 16, 2047, 2048): + for expected_wire_type in range(6): # Highest-numbered wiretype is 5. + field_number, wire_type = wire_format.UnpackTag( + wire_format.PackTag(expected_field_number, expected_wire_type)) + self.assertEqual(expected_field_number, field_number) + self.assertEqual(expected_wire_type, wire_type) + + self.assertRaises(TypeError, wire_format.UnpackTag, None) + self.assertRaises(TypeError, wire_format.UnpackTag, 'abc') + self.assertRaises(TypeError, wire_format.UnpackTag, 0.0) + self.assertRaises(TypeError, wire_format.UnpackTag, object()) + + def testZigZagEncode(self): + Z = wire_format.ZigZagEncode + self.assertEqual(0, Z(0)) + self.assertEqual(1, Z(-1)) + self.assertEqual(2, Z(1)) + self.assertEqual(3, Z(-2)) + self.assertEqual(4, Z(2)) + self.assertEqual(0xfffffffe, Z(0x7fffffff)) + self.assertEqual(0xffffffff, Z(-0x80000000)) + self.assertEqual(0xfffffffffffffffe, Z(0x7fffffffffffffff)) + self.assertEqual(0xffffffffffffffff, Z(-0x8000000000000000)) + + self.assertRaises(TypeError, Z, None) + self.assertRaises(TypeError, Z, 'abcd') + self.assertRaises(TypeError, Z, 0.0) + self.assertRaises(TypeError, Z, object()) + + def testZigZagDecode(self): + Z = wire_format.ZigZagDecode + self.assertEqual(0, Z(0)) + self.assertEqual(-1, Z(1)) + self.assertEqual(1, Z(2)) + self.assertEqual(-2, Z(3)) + self.assertEqual(2, Z(4)) + self.assertEqual(0x7fffffff, Z(0xfffffffe)) + self.assertEqual(-0x80000000, Z(0xffffffff)) + self.assertEqual(0x7fffffffffffffff, Z(0xfffffffffffffffe)) + self.assertEqual(-0x8000000000000000, Z(0xffffffffffffffff)) + + self.assertRaises(TypeError, Z, None) + self.assertRaises(TypeError, Z, 'abcd') + self.assertRaises(TypeError, Z, 0.0) + self.assertRaises(TypeError, Z, object()) + + def NumericByteSizeTestHelper(self, byte_size_fn, value, expected_value_size): + # Use field numbers that cause various byte sizes for the tag information. + for field_number, tag_bytes in ((15, 1), (16, 2), (2047, 2), (2048, 3)): + expected_size = expected_value_size + tag_bytes + actual_size = byte_size_fn(field_number, value) + self.assertEqual(expected_size, actual_size, + 'byte_size_fn: %s, field_number: %d, value: %r\n' + 'Expected: %d, Actual: %d'% ( + byte_size_fn, field_number, value, expected_size, actual_size)) + + def testByteSizeFunctions(self): + # Test all numeric *ByteSize() functions. + NUMERIC_ARGS = [ + # Int32ByteSize(). + [wire_format.Int32ByteSize, 0, 1], + [wire_format.Int32ByteSize, 127, 1], + [wire_format.Int32ByteSize, 128, 2], + [wire_format.Int32ByteSize, -1, 10], + # Int64ByteSize(). + [wire_format.Int64ByteSize, 0, 1], + [wire_format.Int64ByteSize, 127, 1], + [wire_format.Int64ByteSize, 128, 2], + [wire_format.Int64ByteSize, -1, 10], + # UInt32ByteSize(). + [wire_format.UInt32ByteSize, 0, 1], + [wire_format.UInt32ByteSize, 127, 1], + [wire_format.UInt32ByteSize, 128, 2], + [wire_format.UInt32ByteSize, wire_format.UINT32_MAX, 5], + # UInt64ByteSize(). + [wire_format.UInt64ByteSize, 0, 1], + [wire_format.UInt64ByteSize, 127, 1], + [wire_format.UInt64ByteSize, 128, 2], + [wire_format.UInt64ByteSize, wire_format.UINT64_MAX, 10], + # SInt32ByteSize(). + [wire_format.SInt32ByteSize, 0, 1], + [wire_format.SInt32ByteSize, -1, 1], + [wire_format.SInt32ByteSize, 1, 1], + [wire_format.SInt32ByteSize, -63, 1], + [wire_format.SInt32ByteSize, 63, 1], + [wire_format.SInt32ByteSize, -64, 1], + [wire_format.SInt32ByteSize, 64, 2], + # SInt64ByteSize(). + [wire_format.SInt64ByteSize, 0, 1], + [wire_format.SInt64ByteSize, -1, 1], + [wire_format.SInt64ByteSize, 1, 1], + [wire_format.SInt64ByteSize, -63, 1], + [wire_format.SInt64ByteSize, 63, 1], + [wire_format.SInt64ByteSize, -64, 1], + [wire_format.SInt64ByteSize, 64, 2], + # Fixed32ByteSize(). + [wire_format.Fixed32ByteSize, 0, 4], + [wire_format.Fixed32ByteSize, wire_format.UINT32_MAX, 4], + # Fixed64ByteSize(). + [wire_format.Fixed64ByteSize, 0, 8], + [wire_format.Fixed64ByteSize, wire_format.UINT64_MAX, 8], + # SFixed32ByteSize(). + [wire_format.SFixed32ByteSize, 0, 4], + [wire_format.SFixed32ByteSize, wire_format.INT32_MIN, 4], + [wire_format.SFixed32ByteSize, wire_format.INT32_MAX, 4], + # SFixed64ByteSize(). + [wire_format.SFixed64ByteSize, 0, 8], + [wire_format.SFixed64ByteSize, wire_format.INT64_MIN, 8], + [wire_format.SFixed64ByteSize, wire_format.INT64_MAX, 8], + # FloatByteSize(). + [wire_format.FloatByteSize, 0.0, 4], + [wire_format.FloatByteSize, 1000000000.0, 4], + [wire_format.FloatByteSize, -1000000000.0, 4], + # DoubleByteSize(). + [wire_format.DoubleByteSize, 0.0, 8], + [wire_format.DoubleByteSize, 1000000000.0, 8], + [wire_format.DoubleByteSize, -1000000000.0, 8], + # BoolByteSize(). + [wire_format.BoolByteSize, False, 1], + [wire_format.BoolByteSize, True, 1], + # EnumByteSize(). + [wire_format.EnumByteSize, 0, 1], + [wire_format.EnumByteSize, 127, 1], + [wire_format.EnumByteSize, 128, 2], + [wire_format.EnumByteSize, wire_format.UINT32_MAX, 5], + ] + for args in NUMERIC_ARGS: + self.NumericByteSizeTestHelper(*args) + + # Test strings and bytes. + for byte_size_fn in (wire_format.StringByteSize, wire_format.BytesByteSize): + # 1 byte for tag, 1 byte for length, 3 bytes for contents. + self.assertEqual(5, byte_size_fn(10, 'abc')) + # 2 bytes for tag, 1 byte for length, 3 bytes for contents. + self.assertEqual(6, byte_size_fn(16, 'abc')) + # 2 bytes for tag, 2 bytes for length, 128 bytes for contents. + self.assertEqual(132, byte_size_fn(16, 'a' * 128)) + + class MockMessage(object): + def __init__(self, byte_size): + self.byte_size = byte_size + def ByteSize(self): + return self.byte_size + + message_byte_size = 10 + mock_message = MockMessage(byte_size=message_byte_size) + # Test groups. + # (2 * 1) bytes for begin and end tags, plus message_byte_size. + self.assertEqual(2 + message_byte_size, + wire_format.GroupByteSize(1, mock_message)) + # (2 * 2) bytes for begin and end tags, plus message_byte_size. + self.assertEqual(4 + message_byte_size, + wire_format.GroupByteSize(16, mock_message)) + + # Test messages. + # 1 byte for tag, plus 1 byte for length, plus contents. + self.assertEqual(2 + mock_message.byte_size, + wire_format.MessageByteSize(1, mock_message)) + # 2 bytes for tag, plus 1 byte for length, plus contents. + self.assertEqual(3 + mock_message.byte_size, + wire_format.MessageByteSize(16, mock_message)) + # 2 bytes for tag, plus 2 bytes for length, plus contents. + mock_message.byte_size = 128 + self.assertEqual(4 + mock_message.byte_size, + wire_format.MessageByteSize(16, mock_message)) + + + # Test message set item byte size. + # 4 bytes for tags, plus 1 byte for length, plus 1 byte for type_id, + # plus contents. + mock_message.byte_size = 10 + self.assertEqual(mock_message.byte_size + 6, + wire_format.MessageSetItemByteSize(1, mock_message)) + + # 4 bytes for tags, plus 2 bytes for length, plus 1 byte for type_id, + # plus contents. + mock_message.byte_size = 128 + self.assertEqual(mock_message.byte_size + 7, + wire_format.MessageSetItemByteSize(1, mock_message)) + + # 4 bytes for tags, plus 2 bytes for length, plus 2 byte for type_id, + # plus contents. + self.assertEqual(mock_message.byte_size + 8, + wire_format.MessageSetItemByteSize(128, mock_message)) + + # Too-long varint. + self.assertRaises(message.EncodeError, + wire_format.UInt64ByteSize, 1, 1 << 128) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/message.py b/python/google/protobuf/message.py new file mode 100755 index 00000000..9b48f889 --- /dev/null +++ b/python/google/protobuf/message.py @@ -0,0 +1,184 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO(robinson): We should just make these methods all "pure-virtual" and move +# all implementation out, into reflection.py for now. + + +"""Contains an abstract base class for protocol messages.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +from google.protobuf import text_format + +class Error(Exception): pass +class DecodeError(Error): pass +class EncodeError(Error): pass + + +class Message(object): + + """Abstract base class for protocol messages. + + Protocol message classes are almost always generated by the protocol + compiler. These generated types subclass Message and implement the methods + shown below. + + TODO(robinson): Link to an HTML document here. + + TODO(robinson): Document that instances of this class will also + have an Extensions attribute with __getitem__ and __setitem__. + Again, not sure how to best convey this. + + TODO(robinson): Document that the class must also have a static + RegisterExtension(extension_field) method. + Not sure how to best express at this point. + """ + + # TODO(robinson): Document these fields and methods. + + __slots__ = [] + + DESCRIPTOR = None + + def __eq__(self, other_msg): + raise NotImplementedError + + def __ne__(self, other_msg): + # Can't just say self != other_msg, since that would infinitely recurse. :) + return not self == other_msg + + def __str__(self): + return text_format.MessageToString(self) + + def MergeFrom(self, other_msg): + raise NotImplementedError + + def CopyFrom(self, other_msg): + raise NotImplementedError + + def Clear(self): + raise NotImplementedError + + def IsInitialized(self): + raise NotImplementedError + + # TODO(robinson): MergeFromString() should probably return None and be + # implemented in terms of a helper that returns the # of bytes read. Our + # deserialization routines would use the helper when recursively + # deserializing, but the end user would almost always just want the no-return + # MergeFromString(). + + def MergeFromString(self, serialized): + """Merges serialized protocol buffer data into this message. + + When we find a field in |serialized| that is already present + in this message: + - If it's a "repeated" field, we append to the end of our list. + - Else, if it's a scalar, we overwrite our field. + - Else, (it's a nonrepeated composite), we recursively merge + into the existing composite. + + TODO(robinson): Document handling of unknown fields. + + Args: + serialized: Any object that allows us to call buffer(serialized) + to access a string of bytes using the buffer interface. + + TODO(robinson): When we switch to a helper, this will return None. + + Returns: + The number of bytes read from |serialized|. + For non-group messages, this will always be len(serialized), + but for messages which are actually groups, this will + generally be less than len(serialized), since we must + stop when we reach an END_GROUP tag. Note that if + we *do* stop because of an END_GROUP tag, the number + of bytes returned does not include the bytes + for the END_GROUP tag information. + """ + raise NotImplementedError + + def ParseFromString(self, serialized): + """Like MergeFromString(), except we clear the object first.""" + self.Clear() + self.MergeFromString(serialized) + + def SerializeToString(self): + raise NotImplementedError + + # TODO(robinson): Decide whether we like these better + # than auto-generated has_foo() and clear_foo() methods + # on the instances themselves. This way is less consistent + # with C++, but it makes reflection-type access easier and + # reduces the number of magically autogenerated things. + # + # TODO(robinson): Be sure to document (and test) exactly + # which field names are accepted here. Are we case-sensitive? + # What do we do with fields that share names with Python keywords + # like 'lambda' and 'yield'? + # + # nnorwitz says: + # """ + # Typically (in python), an underscore is appended to names that are + # keywords. So they would become lambda_ or yield_. + # """ + def ListFields(self, field_name): + """Returns a list of (FieldDescriptor, value) tuples for all + fields in the message which are not empty. A singular field is non-empty + if HasField() would return true, and a repeated field is non-empty if + it contains at least one element. The fields are ordered by field + number""" + raise NotImplementedError + + def HasField(self, field_name): + raise NotImplementedError + + def ClearField(self, field_name): + raise NotImplementedError + + def HasExtension(self, extension_handle): + raise NotImplementedError + + def ClearExtension(self, extension_handle): + raise NotImplementedError + + def ByteSize(self): + """Returns the serialized size of this message. + Recursively calls ByteSize() on all contained messages. + """ + raise NotImplementedError + + def _SetListener(self, message_listener): + """Internal method used by the protocol message implementation. + Clients should not call this directly. + + Sets a listener that this message will call on certain state transitions. + + The purpose of this method is to register back-edges from children to + parents at runtime, for the purpose of setting "has" bits and + byte-size-dirty bits in the parent and ancestor objects whenever a child or + descendant object is modified. + + If the client wants to disconnect this Message from the object tree, she + explicitly sets callback to None. + + If message_listener is None, unregisters any existing listener. Otherwise, + message_listener must implement the MessageListener interface in + internal/message_listener.py, and we discard any listener registered + via a previous _SetListener() call. + """ + raise NotImplementedError diff --git a/python/google/protobuf/reflection.py b/python/google/protobuf/reflection.py new file mode 100755 index 00000000..75202c4e --- /dev/null +++ b/python/google/protobuf/reflection.py @@ -0,0 +1,1734 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This code is meant to work on Python 2.4 and above only. +# +# TODO(robinson): Helpers for verbose, common checks like seeing if a +# descriptor's cpp_type is CPPTYPE_MESSAGE. + +"""Contains a metaclass and helper functions used to create +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import heapq +import threading +import weakref +# We use "as" to avoid name collisions with variables. +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import message_listener as message_listener_mod +from google.protobuf.internal import wire_format +from google.protobuf import descriptor as descriptor_mod +from google.protobuf import message as message_mod + +_FieldDescriptor = descriptor_mod.FieldDescriptor + + +class GeneratedProtocolMessageType(type): + + """Metaclass for protocol message classes created at runtime from Descriptors. + + We add implementations for all methods described in the Message class. We + also create properties to allow getting/setting all fields in the protocol + message. Finally, we create slots to prevent users from accidentally + "setting" nonexistent fields in the protocol message, which then wouldn't get + serialized / deserialized properly. + + The protocol compiler currently uses this metaclass to create protocol + message classes at runtime. Clients can also manually create their own + classes at runtime, as in this example: + + mydescriptor = Descriptor(.....) + class MyProtoClass(Message): + __metaclass__ = GeneratedProtocolMessageType + DESCRIPTOR = mydescriptor + myproto_instance = MyProtoClass() + myproto.foo_field = 23 + ... + """ + + # Must be consistent with the protocol-compiler code in + # proto2/compiler/internal/generator.*. + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __new__(cls, name, bases, dictionary): + """Custom allocation for runtime-generated class types. + + We override __new__ because this is apparently the only place + where we can meaningfully set __slots__ on the class we're creating(?). + (The interplay between metaclasses and slots is not very well-documented). + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + + Returns: + Newly-allocated class. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + _AddSlots(descriptor, dictionary) + _AddClassAttributesForNestedExtensions(descriptor, dictionary) + superclass = super(GeneratedProtocolMessageType, cls) + return superclass.__new__(cls, name, bases, dictionary) + + def __init__(cls, name, bases, dictionary): + """Here we perform the majority of our work on the class. + We add enum getters, an __init__ method, implementations + of all Message methods, and properties for all fields + in the protocol type. + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + # We act as a "friend" class of the descriptor, setting + # its _concrete_class attribute the first time we use a + # given descriptor to initialize a concrete protocol message + # class. + concrete_class_attr_name = '_concrete_class' + if not hasattr(descriptor, concrete_class_attr_name): + setattr(descriptor, concrete_class_attr_name, cls) + cls._known_extensions = [] + _AddEnumValues(descriptor, cls) + _AddInitMethod(descriptor, cls) + _AddPropertiesForFields(descriptor, cls) + _AddStaticMethods(cls) + _AddMessageMethods(descriptor, cls) + _AddPrivateHelperMethods(cls) + superclass = super(GeneratedProtocolMessageType, cls) + superclass.__init__(cls, name, bases, dictionary) + + +# Stateless helpers for GeneratedProtocolMessageType below. +# Outside clients should not access these directly. +# +# I opted not to make any of these methods on the metaclass, to make it more +# clear that I'm not really using any state there and to keep clients from +# thinking that they have direct access to these construction helpers. + + +def _PropertyName(proto_field_name): + """Returns the name of the public property attribute which + clients can use to get and (in some cases) set the value + of a protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + # TODO(robinson): Escape Python keywords (e.g., yield), and test this support. + # nnorwitz makes my day by writing: + # """ + # FYI. See the keyword module in the stdlib. This could be as simple as: + # + # if keyword.iskeyword(proto_field_name): + # return proto_field_name + "_" + # return proto_field_name + # """ + return proto_field_name + + +def _ValueFieldName(proto_field_name): + """Returns the name of the (internal) instance attribute which objects + should use to store the current value for a given protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + return '_value_' + proto_field_name + + +def _HasFieldName(proto_field_name): + """Returns the name of the (internal) instance attribute which + objects should use to store a boolean telling whether this field + is explicitly set or not. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + return '_has_' + proto_field_name + + +def _AddSlots(message_descriptor, dictionary): + """Adds a __slots__ entry to dictionary, containing the names of all valid + attributes for this message type. + + Args: + message_descriptor: A Descriptor instance describing this message type. + dictionary: Class dictionary to which we'll add a '__slots__' entry. + """ + field_names = [_ValueFieldName(f.name) for f in message_descriptor.fields] + field_names.extend(_HasFieldName(f.name) for f in message_descriptor.fields + if f.label != _FieldDescriptor.LABEL_REPEATED) + field_names.extend(('Extensions', + '_cached_byte_size', + '_cached_byte_size_dirty', + '_called_transition_to_nonempty', + '_listener', + '_lock', '__weakref__')) + dictionary['__slots__'] = field_names + + +def _AddClassAttributesForNestedExtensions(descriptor, dictionary): + extension_dict = descriptor.extensions_by_name + for extension_name, extension_field in extension_dict.iteritems(): + assert extension_name not in dictionary + dictionary[extension_name] = extension_field + + +def _AddEnumValues(descriptor, cls): + """Sets class-level attributes for all enum fields defined in this message. + + Args: + descriptor: Descriptor object for this message type. + cls: Class we're constructing for this message type. + """ + for enum_type in descriptor.enum_types: + for enum_value in enum_type.values: + setattr(cls, enum_value.name, enum_value.number) + + +def _DefaultValueForField(message, field): + """Returns a default value for a field. + + Args: + message: Message instance containing this field, or a weakref proxy + of same. + field: FieldDescriptor object for this field. + + Returns: A default value for this field. May refer back to |message| + via a weak reference. + """ + # TODO(robinson): Only the repeated fields need a reference to 'message' (so + # that they can set the 'has' bit on the containing Message when someone + # append()s a value). We could special-case this, and avoid an extra + # function call on __init__() and Clear() for non-repeated fields. + + # TODO(robinson): Find a better place for the default value assertion in this + # function. No need to repeat them every time the client calls Clear('foo'). + # (We should probably just assert these things once and as early as possible, + # by tightening checking in the descriptor classes.) + if field.label == _FieldDescriptor.LABEL_REPEATED: + if field.default_value != []: + raise ValueError('Repeated field default value not empty list: %s' % ( + field.default_value)) + listener = _Listener(message, None) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # We can't look at _concrete_class yet since it might not have + # been set. (Depends on order in which we initialize the classes). + return _RepeatedCompositeFieldContainer(listener, field.message_type) + else: + return _RepeatedScalarFieldContainer(listener, + _VALUE_CHECKERS[field.cpp_type]) + + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + assert field.default_value is None + + return field.default_value + + +def _AddInitMethod(message_descriptor, cls): + """Adds an __init__ method to cls.""" + fields = message_descriptor.fields + def init(self): + self._cached_byte_size = 0 + self._cached_byte_size_dirty = False + self._listener = message_listener_mod.NullMessageListener() + self._called_transition_to_nonempty = False + # TODO(robinson): We should only create a lock if we really need one + # in this class. + self._lock = threading.Lock() + for field in fields: + default_value = _DefaultValueForField(self, field) + python_field_name = _ValueFieldName(field.name) + setattr(self, python_field_name, default_value) + if field.label != _FieldDescriptor.LABEL_REPEATED: + setattr(self, _HasFieldName(field.name), False) + self.Extensions = _ExtensionDict(self, cls._known_extensions) + + init.__module__ = None + init.__doc__ = None + cls.__init__ = init + + +def _AddPropertiesForFields(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + for field in descriptor.fields: + _AddPropertiesForField(field, cls) + + +def _AddPropertiesForField(field, cls): + """Adds a public property for a protocol message field. + Clients can use this property to get and (in the case + of non-repeated scalar fields) directly set the value + of a protocol message field. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # Catch it if we add other types that we should + # handle specially here. + assert _FieldDescriptor.MAX_CPPTYPE == 10 + + if field.label == _FieldDescriptor.LABEL_REPEATED: + _AddPropertiesForRepeatedField(field, cls) + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + _AddPropertiesForNonRepeatedCompositeField(field, cls) + else: + _AddPropertiesForNonRepeatedScalarField(field, cls) + + +def _AddPropertiesForRepeatedField(field, cls): + """Adds a public property for a "repeated" protocol message field. Clients + can use this property to get the value of the field, which will be either a + _RepeatedScalarFieldContainer or _RepeatedCompositeFieldContainer (see + below). + + Note that when clients add values to these containers, we perform + type-checking in the case of repeated scalar fields, and we also set any + necessary "has" bits as a side-effect. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + property_name = _PropertyName(proto_field_name) + + def getter(self): + return getattr(self, python_field_name) + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % proto_field_name) + + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedScalarField(field, cls): + """Adds a public property for a nonrepeated, scalar protocol message field. + Clients can use this property to get and directly set the value of the field. + Note that when the client sets the value of a field by using this property, + all necessary "has" bits are set as a side-effect, and we also perform + type-checking. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + has_field_name = _HasFieldName(proto_field_name) + property_name = _PropertyName(proto_field_name) + type_checker = _VALUE_CHECKERS[field.cpp_type] + + def getter(self): + return getattr(self, python_field_name) + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + def setter(self, new_value): + type_checker.CheckValue(new_value) + setattr(self, has_field_name, True) + self._MarkByteSizeDirty() + self._MaybeCallTransitionToNonemptyCallback() + setattr(self, python_field_name, new_value) + setter.__module__ = None + setter.__doc__ = 'Setter for %s.' % proto_field_name + + # Add a property to encapsulate the getter/setter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedCompositeField(field, cls): + """Adds a public property for a nonrepeated, composite protocol message field. + A composite field is a "group" or "message" field. + + Clients can use this property to get the value of the field, but cannot + assign to the property directly. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # TODO(robinson): Remove duplication with similar method + # for non-repeated scalars. + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + has_field_name = _HasFieldName(proto_field_name) + property_name = _PropertyName(proto_field_name) + message_type = field.message_type + + def getter(self): + # TODO(robinson): Appropriately scary note about double-checked locking. + field_value = getattr(self, python_field_name) + if field_value is None: + self._lock.acquire() + try: + field_value = getattr(self, python_field_name) + if field_value is None: + field_class = message_type._concrete_class + field_value = field_class() + field_value._SetListener(_Listener(self, has_field_name)) + setattr(self, python_field_name, field_value) + finally: + self._lock.release() + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to composite field ' + '"%s" in protocol message object.' % proto_field_name) + + # Add a property to encapsulate the getter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddStaticMethods(cls): + # TODO(robinson): This probably needs to be thread-safe(?) + def RegisterExtension(extension_handle): + extension_handle.containing_type = cls.DESCRIPTOR + cls._known_extensions.append(extension_handle) + cls.RegisterExtension = staticmethod(RegisterExtension) + + +def _AddListFieldsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + # Ensure that we always list in ascending field-number order. + # For non-extension fields, we can do the sort once, here, at import-time. + # For extensions, we sort on each ListFields() call, though + # we could do better if we have to. + fields = sorted(message_descriptor.fields, key=lambda f: f.number) + has_field_names = (_HasFieldName(f.name) for f in fields) + value_field_names = (_ValueFieldName(f.name) for f in fields) + triplets = zip(has_field_names, value_field_names, fields) + + def ListFields(self): + # We need to list all extension and non-extension fields + # together, in sorted order by field number. + + # Step 0: Get an iterator over all "set" non-extension fields, + # sorted by field number. + # This iterator yields (field_number, field_descriptor, value) tuples. + def SortedSetFieldsIter(): + # Note that triplets is already sorted by field number. + for has_field_name, value_field_name, field_descriptor in triplets: + if field_descriptor.label == _FieldDescriptor.LABEL_REPEATED: + value = getattr(self, _ValueFieldName(field_descriptor.name)) + if len(value) > 0: + yield (field_descriptor.number, field_descriptor, value) + elif getattr(self, _HasFieldName(field_descriptor.name)): + value = getattr(self, _ValueFieldName(field_descriptor.name)) + yield (field_descriptor.number, field_descriptor, value) + sorted_fields = SortedSetFieldsIter() + + # Step 1: Get an iterator over all "set" extension fields, + # sorted by field number. + # This iterator ALSO yields (field_number, field_descriptor, value) tuples. + # TODO(robinson): It's not necessary to repeat this with each + # serialization call. We can do better. + sorted_extension_fields = sorted( + [(f.number, f, v) for f, v in self.Extensions._ListSetExtensions()]) + + # Step 2: Create a composite iterator that merges the extension- + # and non-extension fields, and that still yields fields in + # sorted order. + all_set_fields = _ImergeSorted(sorted_fields, sorted_extension_fields) + + # Step 3: Strip off the field numbers and return. + return [field[1:] for field in all_set_fields] + + cls.ListFields = ListFields + +def _AddHasFieldMethod(cls): + """Helper for _AddMessageMethods().""" + def HasField(self, field_name): + try: + return getattr(self, _HasFieldName(field_name)) + except AttributeError: + raise ValueError('Protocol message has no "%s" field.' % field_name) + cls.HasField = HasField + + +def _AddClearFieldMethod(cls): + """Helper for _AddMessageMethods().""" + def ClearField(self, field_name): + try: + field = self.DESCRIPTOR.fields_by_name[field_name] + except KeyError: + raise ValueError('Protocol message has no "%s" field.' % field_name) + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + has_field_name = _HasFieldName(proto_field_name) + default_value = _DefaultValueForField(self, field) + if field.label == _FieldDescriptor.LABEL_REPEATED: + self._MarkByteSizeDirty() + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + old_field_value = getattr(self, python_field_name) + if old_field_value is not None: + # Snip the old object out of the object tree. + old_field_value._SetListener(None) + if getattr(self, has_field_name): + setattr(self, has_field_name, False) + # Set dirty bit on ourself and parents only if + # we're actually changing state. + self._MarkByteSizeDirty() + setattr(self, python_field_name, default_value) + cls.ClearField = ClearField + + +def _AddClearExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def ClearExtension(self, extension_handle): + self.Extensions._ClearExtension(extension_handle) + cls.ClearExtension = ClearExtension + + +def _AddClearMethod(cls): + """Helper for _AddMessageMethods().""" + def Clear(self): + # Clear fields. + fields = self.DESCRIPTOR.fields + for field in fields: + self.ClearField(field.name) + # Clear extensions. + extensions = self.Extensions._ListSetExtensions() + for extension in extensions: + self.ClearExtension(extension[0]) + cls.Clear = Clear + + +def _AddHasExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def HasExtension(self, extension_handle): + return self.Extensions._HasExtension(extension_handle) + cls.HasExtension = HasExtension + + +def _AddEqualsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __eq__(self, other): + if self is other: + return True + + # Compare all fields contained directly in this message. + for field_descriptor in message_descriptor.fields: + label = field_descriptor.label + property_name = _PropertyName(field_descriptor.name) + # Non-repeated field equality requires matching "has" bits as well + # as having an equal value. + if label != _FieldDescriptor.LABEL_REPEATED: + self_has = self.HasField(property_name) + other_has = other.HasField(property_name) + if self_has != other_has: + return False + if not self_has: + # If the "has" bit for this field is False, we must stop here. + # Otherwise we will recurse forever on recursively-defined protos. + continue + if getattr(self, property_name) != getattr(other, property_name): + return False + + # Compare the extensions present in both messages. + return self.Extensions == other.Extensions + cls.__eq__ = __eq__ + + +def _AddSetListenerMethod(cls): + """Helper for _AddMessageMethods().""" + def SetListener(self, listener): + if listener is None: + self._listener = message_listener_mod.NullMessageListener() + else: + self._listener = listener + cls._SetListener = SetListener + + +def _BytesForNonRepeatedElement(value, field_number, field_type): + """Returns the number of bytes needed to serialize a non-repeated element. + The returned byte count includes space for tag information and any + other additional space associated with serializing value. + + Args: + value: Value we're serializing. + field_number: Field number of this value. (Since the field number + is stored as part of a varint-encoded tag, this has an impact + on the total bytes required to serialize the value). + field_type: The type of the field. One of the TYPE_* constants + within FieldDescriptor. + """ + try: + fn = _TYPE_TO_BYTE_SIZE_FN[field_type] + return fn(field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % field_type) + + +def _AddByteSizeMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def BytesForField(message, field, value): + """Returns the number of bytes required to serialize a single field + in message. The field may be repeated or not, composite or not. + + Args: + message: The Message instance containing a field of the given type. + field: A FieldDescriptor describing the field of interest. + value: The value whose byte size we're interested in. + + Returns: The number of bytes required to serialize the current value + of "field" in "message", including space for tags and any other + necessary information. + """ + + if _MessageSetField(field): + return wire_format.MessageSetItemByteSize(field.number, value) + + field_number, field_type = field.number, field.type + + # Repeated fields. + if field.label == _FieldDescriptor.LABEL_REPEATED: + elements = value + else: + elements = [value] + + size = sum(_BytesForNonRepeatedElement(element, field_number, field_type) + for element in elements) + return size + + fields = message_descriptor.fields + has_field_names = (_HasFieldName(f.name) for f in fields) + zipped = zip(has_field_names, fields) + + def ByteSize(self): + if not self._cached_byte_size_dirty: + return self._cached_byte_size + + size = 0 + # Hardcoded fields first. + for has_field_name, field in zipped: + if (field.label == _FieldDescriptor.LABEL_REPEATED + or getattr(self, has_field_name)): + value = getattr(self, _ValueFieldName(field.name)) + size += BytesForField(self, field, value) + # Extensions next. + for field, value in self.Extensions._ListSetExtensions(): + size += BytesForField(self, field, value) + + self._cached_byte_size = size + self._cached_byte_size_dirty = False + return size + cls.ByteSize = ByteSize + + +def _MessageSetField(field_descriptor): + """Checks if a field should be serialized using the message set wire format. + + Args: + field_descriptor: Descriptor of the field. + + Returns: + True if the field should be serialized using the message set wire format, + false otherwise. + """ + return (field_descriptor.is_extension and + field_descriptor.label != _FieldDescriptor.LABEL_REPEATED and + field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and + field_descriptor.containing_type.GetOptions().message_set_wire_format) + + +def _SerializeValueToEncoder(value, field_number, field_descriptor, encoder): + """Appends the serialization of a single value to encoder. + + Args: + value: Value to serialize. + field_number: Field number of this value. + field_descriptor: Descriptor of the field to serialize. + encoder: encoder.Encoder object to which we should serialize this value. + """ + if _MessageSetField(field_descriptor): + encoder.AppendMessageSetItem(field_number, value) + return + + try: + method = _TYPE_TO_SERIALIZE_METHOD[field_descriptor.type] + method(encoder, field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % + field_descriptor.type) + + +def _ImergeSorted(*streams): + """Merges N sorted iterators into a single sorted iterator. + Each element in streams must be an iterable that yields + its elements in sorted order, and the elements contained + in each stream must all be comparable. + + There may be repeated elements in the component streams or + across the streams; the repeated elements will all be repeated + in the merged iterator as well. + + I believe that the heapq module at HEAD in the Python + sources has a method like this, but for now we roll our own. + """ + iters = [iter(stream) for stream in streams] + heap = [] + for index, it in enumerate(iters): + try: + heap.append((it.next(), index)) + except StopIteration: + pass + heapq.heapify(heap) + + while heap: + smallest_value, idx = heap[0] + yield smallest_value + try: + next_element = iters[idx].next() + heapq.heapreplace(heap, (next_element, idx)) + except StopIteration: + heapq.heappop(heap) + + +def _AddSerializeToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + Encoder = encoder.Encoder + + def SerializeToString(self): + encoder = Encoder() + # We need to serialize all extension and non-extension fields + # together, in sorted order by field number. + + # Step 3: Iterate over all extension and non-extension fields, sorted + # in order of tag number, and serialize each one to the wire. + for field_descriptor, field_value in self.ListFields(): + if field_descriptor.label == _FieldDescriptor.LABEL_REPEATED: + repeated_value = field_value + else: + repeated_value = [field_value] + for element in repeated_value: + _SerializeValueToEncoder(element, field_descriptor.number, + field_descriptor, encoder) + return encoder.ToString() + cls.SerializeToString = SerializeToString + + +def _WireTypeForFieldType(field_type): + """Given a field type, returns the expected wire type.""" + try: + return _FIELD_TYPE_TO_WIRE_TYPE[field_type] + except KeyError: + raise message_mod.DecodeError('Unknown field type: %d' % field_type) + + +def _RecursivelyMerge(field_number, field_type, decoder, message): + """Decodes a message from decoder into message. + message is either a group or a nested message within some containing + protocol message. If it's a group, we use the group protocol to + deserialize, and if it's a nested message, we use the nested-message + protocol. + + Args: + field_number: The field number of message in its enclosing protocol buffer. + field_type: The field type of message. Must be either TYPE_MESSAGE + or TYPE_GROUP. + decoder: Decoder to read from. + message: Message to deserialize into. + """ + if field_type == _FieldDescriptor.TYPE_MESSAGE: + decoder.ReadMessageInto(message) + elif field_type == _FieldDescriptor.TYPE_GROUP: + decoder.ReadGroupInto(field_number, message) + else: + raise message_mod.DecodeError('Unexpected field type: %d' % field_type) + + +def _DeserializeScalarFromDecoder(field_type, decoder): + """Deserializes a scalar of the requested type from decoder. field_type must + be a scalar (non-group, non-message) FieldDescriptor.FIELD_* constant. + """ + try: + method = _TYPE_TO_DESERIALIZE_METHOD[field_type] + return method(decoder) + except KeyError: + raise message_mod.DecodeError('Unrecognized field type: %d' % field_type) + + +def _SkipField(field_number, wire_type, decoder): + """Skips a field with the specified wire type. + + Args: + field_number: Tag number of the field to skip. + wire_type: Wire type of the field to skip. + decoder: Decoder used to deserialize the messsage. It must be positioned + just after reading the the tag and wire type of the field. + """ + if wire_type == wire_format.WIRETYPE_VARINT: + decoder.ReadInt32() + elif wire_type == wire_format.WIRETYPE_FIXED64: + decoder.ReadFixed64() + elif wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED: + decoder.SkipBytes(decoder.ReadInt32()) + elif wire_type == wire_format.WIRETYPE_START_GROUP: + _SkipGroup(field_number, decoder) + elif wire_type == wire_format.WIRETYPE_END_GROUP: + pass + elif wire_type == wire_format.WIRETYPE_FIXED32: + decoder.ReadFixed32() + else: + raise message_mod.DecodeError('Unexpected wire type: %d' % wire_type) + + +def _SkipGroup(group_number, decoder): + """Skips a nested group from the decoder. + + Args: + group_number: Tag number of the group to skip. + decoder: Decoder used to deserialize the message. It must be positioned + exactly at the beginning of the message that should be skipped. + """ + while True: + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if (wire_type == wire_format.WIRETYPE_END_GROUP and + field_number == group_number): + return + _SkipField(field_number, wire_type, decoder) + + +def _DeserializeMessageSetItem(message, decoder): + """Deserializes a message using the message set wire format. + + Args: + message: Message to be parsed to. + decoder: The decoder to be used to deserialize encoded data. Note that the + decoder should be positioned just after reading the START_GROUP tag that + began the messageset item. + """ + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if wire_type != wire_format.WIRETYPE_VARINT or field_number != 2: + raise message_mod.DecodeError( + 'Incorrect message set wire format. ' + 'wire_type: %d, field_number: %d' % (wire_type, field_number)) + + type_id = decoder.ReadInt32() + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if wire_type != wire_format.WIRETYPE_LENGTH_DELIMITED or field_number != 3: + raise message_mod.DecodeError( + 'Incorrect message set wire format. ' + 'wire_type: %d, field_number: %d' % (wire_type, field_number)) + + extension_dict = message.Extensions + extensions_by_number = extension_dict._AllExtensionsByNumber() + if type_id not in extensions_by_number: + _SkipField(field_number, wire_type, decoder) + return + + field_descriptor = extensions_by_number[type_id] + value = extension_dict[field_descriptor] + decoder.ReadMessageInto(value) + # Read the END_GROUP tag. + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if wire_type != wire_format.WIRETYPE_END_GROUP or field_number != 1: + raise message_mod.DecodeError( + 'Incorrect message set wire format. ' + 'wire_type: %d, field_number: %d' % (wire_type, field_number)) + + +def _DeserializeOneEntity(message_descriptor, message, decoder): + """Deserializes the next wire entity from decoder into message. + The next wire entity is either a scalar or a nested message, + and may also be an element in a repeated field (the wire encoding + is the same). + + Args: + message_descriptor: A Descriptor instance describing all fields + in message. + message: The Message instance into which we're decoding our fields. + decoder: The Decoder we're using to deserialize encoded data. + + Returns: The number of bytes read from decoder during this method. + """ + initial_position = decoder.Position() + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + extension_dict = message.Extensions + extensions_by_number = extension_dict._AllExtensionsByNumber() + if field_number in message_descriptor.fields_by_number: + # Non-extension field. + field_descriptor = message_descriptor.fields_by_number[field_number] + value = getattr(message, _PropertyName(field_descriptor.name)) + def nonextension_setter_fn(scalar): + setattr(message, _PropertyName(field_descriptor.name), scalar) + scalar_setter_fn = nonextension_setter_fn + elif field_number in extensions_by_number: + # Extension field. + field_descriptor = extensions_by_number[field_number] + value = extension_dict[field_descriptor] + def extension_setter_fn(scalar): + extension_dict[field_descriptor] = scalar + scalar_setter_fn = extension_setter_fn + elif wire_type == wire_format.WIRETYPE_END_GROUP: + # We assume we're being parsed as the group that's ended. + return 0 + elif (wire_type == wire_format.WIRETYPE_START_GROUP and + field_number == 1 and + message_descriptor.GetOptions().message_set_wire_format): + # A Message Set item. + _DeserializeMessageSetItem(message, decoder) + return decoder.Position() - initial_position + else: + _SkipField(field_number, wire_type, decoder) + return decoder.Position() - initial_position + + # If we reach this point, we've identified the field as either + # hardcoded or extension, and set |field_descriptor|, |scalar_setter_fn|, + # and |value| appropriately. Now actually deserialize the thing. + # + # field_descriptor: Describes the field we're deserializing. + # value: The value currently stored in the field to deserialize. + # Used only if the field is composite and/or repeated. + # scalar_setter_fn: A function F such that F(scalar) will + # set a nonrepeated scalar value for this field. Used only + # if this field is a nonrepeated scalar. + + field_number = field_descriptor.number + field_type = field_descriptor.type + expected_wire_type = _WireTypeForFieldType(field_type) + if wire_type != expected_wire_type: + # Need to fill in uninterpreted_bytes. Work for the next CL. + raise RuntimeError('TODO(robinson): Wiretype mismatches not handled.') + + property_name = _PropertyName(field_descriptor.name) + label = field_descriptor.label + cpp_type = field_descriptor.cpp_type + + # Nonrepeated scalar. Just set the field directly. + if (label != _FieldDescriptor.LABEL_REPEATED + and cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE): + scalar_setter_fn(_DeserializeScalarFromDecoder(field_type, decoder)) + return decoder.Position() - initial_position + + # Nonrepeated composite. Recursively deserialize. + if label != _FieldDescriptor.LABEL_REPEATED: + composite = value + _RecursivelyMerge(field_number, field_type, decoder, composite) + return decoder.Position() - initial_position + + # Now we know we're dealing with a repeated field of some kind. + element_list = value + + if cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE: + # Repeated scalar. + element_list.append(_DeserializeScalarFromDecoder(field_type, decoder)) + return decoder.Position() - initial_position + else: + # Repeated composite. + composite = element_list.add() + _RecursivelyMerge(field_number, field_type, decoder, composite) + return decoder.Position() - initial_position + + +def _FieldOrExtensionValues(message, field_or_extension): + """Retrieves the list of values for the specified field or extension. + + The target field or extension can be optional, required or repeated, but it + must have value(s) set. The assumption is that the target field or extension + is set (e.g. _HasFieldOrExtension holds true). + + Args: + message: Message which contains the target field or extension. + field_or_extension: Field or extension for which the list of values is + required. Must be an instance of FieldDescriptor. + + Returns: + A list of values for the specified field or extension. This list will only + contain a single element if the field is non-repeated. + """ + if field_or_extension.is_extension: + value = message.Extensions[field_or_extension] + else: + value = getattr(message, _ValueFieldName(field_or_extension.name)) + if field_or_extension.label != _FieldDescriptor.LABEL_REPEATED: + return [value] + else: + # In this case value is a list or repeated values. + return value + + +def _HasFieldOrExtension(message, field_or_extension): + """Checks if a message has the specified field or extension set. + + The field or extension specified can be optional, required or repeated. If + it is repeated, this function returns True. Otherwise it checks the has bit + of the field or extension. + + Args: + message: Message which contains the target field or extension. + field_or_extension: Field or extension to check. This must be a + FieldDescriptor instance. + + Returns: + True if the message has a value set for the specified field or extension, + or if the field or extension is repeated. + """ + if field_or_extension.label == _FieldDescriptor.LABEL_REPEATED: + return True + if field_or_extension.is_extension: + return message.HasExtension(field_or_extension) + else: + return message.HasField(field_or_extension.name) + + +def _IsFieldOrExtensionInitialized(message, field): + """Checks if a message field or extension is initialized. + + Args: + message: The message which contains the field or extension. + field: Field or extension to check. This must be a FieldDescriptor instance. + + Returns: + True if the field/extension can be considered initialized. + """ + # If the field is required and is not set, it isn't initialized. + if field.label == _FieldDescriptor.LABEL_REQUIRED: + if not _HasFieldOrExtension(message, field): + return False + + # If the field is optional and is not set, or if it + # isn't a submessage then the field is initialized. + if field.label == _FieldDescriptor.LABEL_OPTIONAL: + if not _HasFieldOrExtension(message, field): + return True + if field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE: + return True + + # The field is set and is either a single or a repeated submessage. + messages = _FieldOrExtensionValues(message, field) + # If all submessages in this field are initialized, the field is + # considered initialized. + for message in messages: + if not message.IsInitialized(): + return False + return True + + +def _AddMergeFromStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + Decoder = decoder.Decoder + def MergeFromString(self, serialized): + decoder = Decoder(serialized) + byte_count = 0 + while not decoder.EndOfStream(): + bytes_read = _DeserializeOneEntity(message_descriptor, self, decoder) + if not bytes_read: + break + byte_count += bytes_read + return byte_count + cls.MergeFromString = MergeFromString + + +def _AddIsInitializedMethod(message_descriptor, cls): + """Adds the IsInitialized method to the protocol message class.""" + def IsInitialized(self): + fields_and_extensions = [] + fields_and_extensions.extend(message_descriptor.fields) + fields_and_extensions.extend( + self.Extensions._AllExtensionsByNumber().values()) + for field_or_extension in fields_and_extensions: + if not _IsFieldOrExtensionInitialized(self, field_or_extension): + return False + return True + cls.IsInitialized = IsInitialized + + +def _AddMessageMethods(message_descriptor, cls): + """Adds implementations of all Message methods to cls.""" + + # TODO(robinson): Add support for remaining Message methods. + + _AddListFieldsMethod(message_descriptor, cls) + _AddHasFieldMethod(cls) + _AddClearFieldMethod(cls) + _AddClearExtensionMethod(cls) + _AddClearMethod(cls) + _AddHasExtensionMethod(cls) + _AddEqualsMethod(message_descriptor, cls) + _AddSetListenerMethod(cls) + _AddByteSizeMethod(message_descriptor, cls) + _AddSerializeToStringMethod(message_descriptor, cls) + _AddMergeFromStringMethod(message_descriptor, cls) + _AddIsInitializedMethod(message_descriptor, cls) + + +def _AddPrivateHelperMethods(cls): + """Adds implementation of private helper methods to cls.""" + + def MaybeCallTransitionToNonemptyCallback(self): + """Calls self._listener.TransitionToNonempty() the first time this + method is called. On all subsequent calls, this is a no-op. + """ + if not self._called_transition_to_nonempty: + self._listener.TransitionToNonempty() + self._called_transition_to_nonempty = True + cls._MaybeCallTransitionToNonemptyCallback = ( + MaybeCallTransitionToNonemptyCallback) + + def MarkByteSizeDirty(self): + """Sets the _cached_byte_size_dirty bit to true, + and propagates this to our listener iff this was a state change. + """ + if not self._cached_byte_size_dirty: + self._cached_byte_size_dirty = True + self._listener.ByteSizeDirty() + cls._MarkByteSizeDirty = MarkByteSizeDirty + + +class _Listener(object): + + """MessageListener implementation that a parent message registers with its + child message. + + In order to support semantics like: + + foo.bar.baz = 23 + assert foo.HasField('bar') + + ...child objects must have back references to their parents. + This helper class is at the heart of this support. + """ + + def __init__(self, parent_message, has_field_name): + """Args: + parent_message: The message whose _MaybeCallTransitionToNonemptyCallback() + and _MarkByteSizeDirty() methods we should call when we receive + TransitionToNonempty() and ByteSizeDirty() messages. + has_field_name: The name of the "has" field that we should set in + the parent message when we receive a TransitionToNonempty message, + or None if there's no "has" field to set. (This will be the case + for child objects in "repeated" fields). + """ + # This listener establishes a back reference from a child (contained) object + # to its parent (containing) object. We make this a weak reference to avoid + # creating cyclic garbage when the client finishes with the 'parent' object + # in the tree. + if isinstance(parent_message, weakref.ProxyType): + self._parent_message_weakref = parent_message + else: + self._parent_message_weakref = weakref.proxy(parent_message) + self._has_field_name = has_field_name + + def TransitionToNonempty(self): + try: + if self._has_field_name is not None: + setattr(self._parent_message_weakref, self._has_field_name, True) + # Propagate the signal to our parents iff this is the first field set. + self._parent_message_weakref._MaybeCallTransitionToNonemptyCallback() + except ReferenceError: + # We can get here if a client has kept a reference to a child object, + # and is now setting a field on it, but the child's parent has been + # garbage-collected. This is not an error. + pass + + def ByteSizeDirty(self): + try: + self._parent_message_weakref._MarkByteSizeDirty() + except ReferenceError: + # Same as above. + pass + + +# TODO(robinson): Move elsewhere? +# TODO(robinson): Provide a clear() method here in addition to ClearField()? +class _RepeatedScalarFieldContainer(object): + + """Simple, type-checked, list-like container for holding repeated scalars. + """ + + def __init__(self, message_listener, type_checker): + """ + Args: + message_listener: A MessageListener implementation. + The _RepeatedScalarFieldContaininer will call this object's + TransitionToNonempty() method when it transitions from being empty to + being nonempty. + type_checker: A _ValueChecker instance to run on elements inserted + into this container. + """ + self._message_listener = message_listener + self._type_checker = type_checker + self._values = [] + + def append(self, elem): + self._type_checker.CheckValue(elem) + self._values.append(elem) + self._message_listener.ByteSizeDirty() + if len(self._values) == 1: + self._message_listener.TransitionToNonempty() + + # List-like __getitem__() support also makes us iterable (via "iter(foo)" + # or implicitly via "for i in mylist:") for free. + def __getitem__(self, key): + return self._values[key] + + def __setitem__(self, key, value): + # No need to call TransitionToNonempty(), since if we're able to + # set the element at this index, we were already nonempty before + # this method was called. + self._message_listener.ByteSizeDirty() + self._type_checker.CheckValue(value) + self._values[key] = value + + def __len__(self): + return len(self._values) + + def __eq__(self, other): + if self is other: + return True + # Special case for the same type which should be common and fast. + if isinstance(other, self.__class__): + return other._values == self._values + # We are presumably comparing against some other sequence type. + return other == self._values + + def __ne__(self, other): + # Can't use != here since it would infinitely recurse. + return not self == other + + +# TODO(robinson): Move elsewhere? +# TODO(robinson): Provide a clear() method here in addition to ClearField()? +# TODO(robinson): Unify common functionality with +# _RepeatedScalarFieldContaininer? +class _RepeatedCompositeFieldContainer(object): + + """Simple, list-like container for holding repeated composite fields. + """ + + def __init__(self, message_listener, message_descriptor): + """Note that we pass in a descriptor instead of the generated directly, + since at the time we construct a _RepeatedCompositeFieldContainer we + haven't yet necessarily initialized the type that will be contained in the + container. + + Args: + message_listener: A MessageListener implementation. + The _RepeatedCompositeFieldContainer will call this object's + TransitionToNonempty() method when it transitions from being empty to + being nonempty. + message_descriptor: A Descriptor instance describing the protocol type + that should be present in this container. We'll use the + _concrete_class field of this descriptor when the client calls add(). + """ + self._message_listener = message_listener + self._message_descriptor = message_descriptor + self._values = [] + + def add(self): + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + self._values.append(new_element) + self._message_listener.ByteSizeDirty() + self._message_listener.TransitionToNonempty() + return new_element + + # List-like __getitem__() support also makes us iterable (via "iter(foo)" + # or implicitly via "for i in mylist:") for free. + def __getitem__(self, key): + return self._values[key] + + def __len__(self): + return len(self._values) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, self.__class__): + raise TypeError('Can only compare repeated composite fields against ' + 'other repeated composite fields.') + return self._values == other._values + + def __ne__(self, other): + # Can't use != here since it would infinitely recurse. + return not self == other + + # TODO(robinson): Implement, document, and test slicing support. + + +# TODO(robinson): Move elsewhere? This file is getting pretty ridiculous... +# TODO(robinson): Unify error handling of "unknown extension" crap. +# TODO(robinson): There's so much similarity between the way that +# extensions behave and the way that normal fields behave that it would +# be really nice to unify more code. It's not immediately obvious +# how to do this, though, and I'd rather get the full functionality +# implemented (and, crucially, get all the tests and specs fleshed out +# and passing), and then come back to this thorny unification problem. +# TODO(robinson): Support iteritems()-style iteration over all +# extensions with the "has" bits turned on? +class _ExtensionDict(object): + + """Dict-like container for supporting an indexable "Extensions" + field on proto instances. + + Note that in all cases we expect extension handles to be + FieldDescriptors. + """ + + class _ExtensionListener(object): + + """Adapts an _ExtensionDict to behave as a MessageListener.""" + + def __init__(self, extension_dict, handle_id): + self._extension_dict = extension_dict + self._handle_id = handle_id + + def TransitionToNonempty(self): + self._extension_dict._SubmessageTransitionedToNonempty(self._handle_id) + + def ByteSizeDirty(self): + self._extension_dict._SubmessageByteSizeBecameDirty() + + # TODO(robinson): Somewhere, we need to blow up if people + # try to register two extensions with the same field number. + # (And we need a test for this of course). + + def __init__(self, extended_message, known_extensions): + """extended_message: Message instance for which we are the Extensions dict. + known_extensions: Iterable of known extension handles. + These must be FieldDescriptors. + """ + # We keep a weak reference to extended_message, since + # it has a reference to this instance in turn. + self._extended_message = weakref.proxy(extended_message) + # We make a deep copy of known_extensions to avoid any + # thread-safety concerns, since the argument passed in + # is the global (class-level) dict of known extensions for + # this type of message, which could be modified at any time + # via a RegisterExtension() call. + # + # This dict maps from handle id to handle (a FieldDescriptor). + # + # XXX + # TODO(robinson): This isn't good enough. The client could + # instantiate an object in module A, then afterward import + # module B and pass the instance to B.Foo(). If B imports + # an extender of this proto and then tries to use it, B + # will get a KeyError, even though the extension *is* registered + # at the time of use. + # XXX + self._known_extensions = dict((id(e), e) for e in known_extensions) + # Read lock around self._values, which may be modified by multiple + # concurrent readers in the conceptually "const" __getitem__ method. + # So, we grab this lock in every "read-only" method to ensure + # that concurrent read access is safe without external locking. + self._lock = threading.Lock() + # Maps from extension handle ID to current value of that extension. + self._values = {} + # Maps from extension handle ID to a boolean "has" bit, but only + # for non-repeated extension fields. + keys = (id for id, extension in self._known_extensions.iteritems() + if extension.label != _FieldDescriptor.LABEL_REPEATED) + self._has_bits = dict.fromkeys(keys, False) + + def __getitem__(self, extension_handle): + """Returns the current value of the given extension handle.""" + # We don't care as much about keeping critical sections short in the + # extension support, since it's presumably much less of a common case. + self._lock.acquire() + try: + handle_id = id(extension_handle) + if handle_id not in self._known_extensions: + raise KeyError('Extension not known to this class') + if handle_id not in self._values: + self._AddMissingHandle(extension_handle, handle_id) + return self._values[handle_id] + finally: + self._lock.release() + + def __eq__(self, other): + # We have to grab read locks since we're accessing _values + # in a "const" method. See the comment in the constructor. + if self is other: + return True + self._lock.acquire() + try: + other._lock.acquire() + try: + if self._has_bits != other._has_bits: + return False + # If there's a "has" bit, then only compare values where it is true. + for k, v in self._values.iteritems(): + if self._has_bits.get(k, False) and v != other._values[k]: + return False + return True + finally: + other._lock.release() + finally: + self._lock.release() + + def __ne__(self, other): + return not self == other + + # Note that this is only meaningful for non-repeated, scalar extension + # fields. Note also that we may have to call + # MaybeCallTransitionToNonemptyCallback() when we do successfully set a field + # this way, to set any necssary "has" bits in the ancestors of the extended + # message. + def __setitem__(self, extension_handle, value): + """If extension_handle specifies a non-repeated, scalar extension + field, sets the value of that field. + """ + handle_id = id(extension_handle) + if handle_id not in self._known_extensions: + raise KeyError('Extension not known to this class') + field = extension_handle # Just shorten the name. + if (field.label == _FieldDescriptor.LABEL_OPTIONAL + and field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE): + # It's slightly wasteful to lookup the type checker each time, + # but we expect this to be a vanishingly uncommon case anyway. + type_checker = _VALUE_CHECKERS[field.cpp_type] + type_checker.CheckValue(value) + self._values[handle_id] = value + self._has_bits[handle_id] = True + self._extended_message._MarkByteSizeDirty() + self._extended_message._MaybeCallTransitionToNonemptyCallback() + else: + raise TypeError('Extension is repeated and/or a composite type.') + + def _AddMissingHandle(self, extension_handle, handle_id): + """Helper internal to ExtensionDict.""" + # Special handling for non-repeated message extensions, which (like + # normal fields of this kind) are initialized lazily. + # REQUIRES: _lock already held. + cpp_type = extension_handle.cpp_type + label = extension_handle.label + if (cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + and label != _FieldDescriptor.LABEL_REPEATED): + self._AddMissingNonRepeatedCompositeHandle(extension_handle, handle_id) + else: + self._values[handle_id] = _DefaultValueForField( + self._extended_message, extension_handle) + + def _AddMissingNonRepeatedCompositeHandle(self, extension_handle, handle_id): + """Helper internal to ExtensionDict.""" + # REQUIRES: _lock already held. + value = extension_handle.message_type._concrete_class() + value._SetListener(_ExtensionDict._ExtensionListener(self, handle_id)) + self._values[handle_id] = value + + def _SubmessageTransitionedToNonempty(self, handle_id): + """Called when a submessage with a given handle id first transitions to + being nonempty. Called by _ExtensionListener. + """ + assert handle_id in self._has_bits + self._has_bits[handle_id] = True + self._extended_message._MaybeCallTransitionToNonemptyCallback() + + def _SubmessageByteSizeBecameDirty(self): + """Called whenever a submessage's cached byte size becomes invalid + (goes from being "clean" to being "dirty"). Called by _ExtensionListener. + """ + self._extended_message._MarkByteSizeDirty() + + # We may wish to widen the public interface of Message.Extensions + # to expose some of this private functionality in the future. + # For now, we make all this functionality module-private and just + # implement what we need for serialization/deserialization, + # HasField()/ClearField(), etc. + + def _HasExtension(self, extension_handle): + """Method for internal use by this module. + Returns true iff we "have" this extension in the sense of the + "has" bit being set. + """ + handle_id = id(extension_handle) + # Note that this is different from the other checks. + if handle_id not in self._has_bits: + raise KeyError('Extension not known to this class, or is repeated field.') + return self._has_bits[handle_id] + + # Intentionally pretty similar to ClearField() above. + def _ClearExtension(self, extension_handle): + """Method for internal use by this module. + Clears the specified extension, unsetting its "has" bit. + """ + handle_id = id(extension_handle) + if handle_id not in self._known_extensions: + raise KeyError('Extension not known to this class') + default_value = _DefaultValueForField(self._extended_message, + extension_handle) + if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: + self._extended_message._MarkByteSizeDirty() + else: + cpp_type = extension_handle.cpp_type + if cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if handle_id in self._values: + # Future modifications to this object shouldn't set any + # "has" bits here. + self._values[handle_id]._SetListener(None) + if self._has_bits[handle_id]: + self._has_bits[handle_id] = False + self._extended_message._MarkByteSizeDirty() + if handle_id in self._values: + del self._values[handle_id] + + def _ListSetExtensions(self): + """Method for internal use by this module. + + Returns an sequence of all extensions that are currently "set" + in this extension dict. A "set" extension is a repeated extension, + or a non-repeated extension with its "has" bit set. + + The returned sequence contains (field_descriptor, value) pairs, + where value is the current value of the extension with the given + field descriptor. + + The sequence values are in arbitrary order. + """ + self._lock.acquire() # Read-only methods must lock around self._values. + try: + set_extensions = [] + for handle_id, value in self._values.iteritems(): + handle = self._known_extensions[handle_id] + if (handle.label == _FieldDescriptor.LABEL_REPEATED + or self._has_bits[handle_id]): + set_extensions.append((handle, value)) + return set_extensions + finally: + self._lock.release() + + def _AllExtensionsByNumber(self): + """Method for internal use by this module. + + Returns: A dict mapping field_number to (handle, field_descriptor), + for *all* registered extensions for this dict. + """ + # TODO(robinson): Precompute and store this away. Note that we'll have to + # be careful when we move away from having _known_extensions as a + # deep-copied member of this object. + return dict((f.number, f) for f in self._known_extensions.itervalues()) + + +# None of the typecheckers below make any attempt to guard against people +# subclassing builtin types and doing weird things. We're not trying to +# protect against malicious clients here, just people accidentally shooting +# themselves in the foot in obvious ways. + +class _TypeChecker(object): + + """Type checker used to catch type errors as early as possible + when the client is setting scalar fields in protocol messages. + """ + + def __init__(self, *acceptable_types): + self._acceptable_types = acceptable_types + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, self._acceptable_types): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), self._acceptable_types)) + raise TypeError(message) + + +# _IntValueChecker and its subclasses perform integer type-checks +# and bounds-checks. +class _IntValueChecker(object): + + """Checker used for integer fields. Performs type-check and range check.""" + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, (int, long)): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (int, long))) + raise TypeError(message) + if not self._MIN <= proposed_value <= self._MAX: + raise ValueError('Value out of range: %d' % proposed_value) + +class _Int32ValueChecker(_IntValueChecker): + # We're sure to use ints instead of longs here since comparison may be more + # efficient. + _MIN = -2147483648 + _MAX = 2147483647 + +class _Uint32ValueChecker(_IntValueChecker): + _MIN = 0 + _MAX = (1 << 32) - 1 + +class _Int64ValueChecker(_IntValueChecker): + _MIN = -(1 << 63) + _MAX = (1 << 63) - 1 + +class _Uint64ValueChecker(_IntValueChecker): + _MIN = 0 + _MAX = (1 << 64) - 1 + + +# Type-checkers for all scalar CPPTYPEs. +_VALUE_CHECKERS = { + _FieldDescriptor.CPPTYPE_INT32: _Int32ValueChecker(), + _FieldDescriptor.CPPTYPE_INT64: _Int64ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT32: _Uint32ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT64: _Uint64ValueChecker(), + _FieldDescriptor.CPPTYPE_DOUBLE: _TypeChecker( + float, int, long), + _FieldDescriptor.CPPTYPE_FLOAT: _TypeChecker( + float, int, long), + _FieldDescriptor.CPPTYPE_BOOL: _TypeChecker(bool, int), + _FieldDescriptor.CPPTYPE_ENUM: _Int32ValueChecker(), + _FieldDescriptor.CPPTYPE_STRING: _TypeChecker(str), + } + + +# Map from field type to a function F, such that F(field_num, value) +# gives the total byte size for a value of the given type. This +# byte size includes tag information and any other additional space +# associated with serializing "value". +_TYPE_TO_BYTE_SIZE_FN = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.DoubleByteSize, + _FieldDescriptor.TYPE_FLOAT: wire_format.FloatByteSize, + _FieldDescriptor.TYPE_INT64: wire_format.Int64ByteSize, + _FieldDescriptor.TYPE_UINT64: wire_format.UInt64ByteSize, + _FieldDescriptor.TYPE_INT32: wire_format.Int32ByteSize, + _FieldDescriptor.TYPE_FIXED64: wire_format.Fixed64ByteSize, + _FieldDescriptor.TYPE_FIXED32: wire_format.Fixed32ByteSize, + _FieldDescriptor.TYPE_BOOL: wire_format.BoolByteSize, + _FieldDescriptor.TYPE_STRING: wire_format.StringByteSize, + _FieldDescriptor.TYPE_GROUP: wire_format.GroupByteSize, + _FieldDescriptor.TYPE_MESSAGE: wire_format.MessageByteSize, + _FieldDescriptor.TYPE_BYTES: wire_format.BytesByteSize, + _FieldDescriptor.TYPE_UINT32: wire_format.UInt32ByteSize, + _FieldDescriptor.TYPE_ENUM: wire_format.EnumByteSize, + _FieldDescriptor.TYPE_SFIXED32: wire_format.SFixed32ByteSize, + _FieldDescriptor.TYPE_SFIXED64: wire_format.SFixed64ByteSize, + _FieldDescriptor.TYPE_SINT32: wire_format.SInt32ByteSize, + _FieldDescriptor.TYPE_SINT64: wire_format.SInt64ByteSize + } + +# Maps from field type to an unbound Encoder method F, such that +# F(encoder, field_number, value) will append the serialization +# of a value of this type to the encoder. +_Encoder = encoder.Encoder +_TYPE_TO_SERIALIZE_METHOD = { + _FieldDescriptor.TYPE_DOUBLE: _Encoder.AppendDouble, + _FieldDescriptor.TYPE_FLOAT: _Encoder.AppendFloat, + _FieldDescriptor.TYPE_INT64: _Encoder.AppendInt64, + _FieldDescriptor.TYPE_UINT64: _Encoder.AppendUInt64, + _FieldDescriptor.TYPE_INT32: _Encoder.AppendInt32, + _FieldDescriptor.TYPE_FIXED64: _Encoder.AppendFixed64, + _FieldDescriptor.TYPE_FIXED32: _Encoder.AppendFixed32, + _FieldDescriptor.TYPE_BOOL: _Encoder.AppendBool, + _FieldDescriptor.TYPE_STRING: _Encoder.AppendString, + _FieldDescriptor.TYPE_GROUP: _Encoder.AppendGroup, + _FieldDescriptor.TYPE_MESSAGE: _Encoder.AppendMessage, + _FieldDescriptor.TYPE_BYTES: _Encoder.AppendBytes, + _FieldDescriptor.TYPE_UINT32: _Encoder.AppendUInt32, + _FieldDescriptor.TYPE_ENUM: _Encoder.AppendEnum, + _FieldDescriptor.TYPE_SFIXED32: _Encoder.AppendSFixed32, + _FieldDescriptor.TYPE_SFIXED64: _Encoder.AppendSFixed64, + _FieldDescriptor.TYPE_SINT32: _Encoder.AppendSInt32, + _FieldDescriptor.TYPE_SINT64: _Encoder.AppendSInt64, + } + +# Maps from field type to expected wiretype. +_FIELD_TYPE_TO_WIRE_TYPE = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FLOAT: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_INT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_UINT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_INT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_FIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_BOOL: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_STRING: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_GROUP: wire_format.WIRETYPE_START_GROUP, + _FieldDescriptor.TYPE_MESSAGE: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_BYTES: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_UINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_ENUM: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SFIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_SFIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_SINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SINT64: wire_format.WIRETYPE_VARINT, + } + +# Maps from field type to an unbound Decoder method F, +# such that F(decoder) will read a field of the requested type. +# +# Note that Message and Group are intentionally missing here. +# They're handled by _RecursivelyMerge(). +_Decoder = decoder.Decoder +_TYPE_TO_DESERIALIZE_METHOD = { + _FieldDescriptor.TYPE_DOUBLE: _Decoder.ReadDouble, + _FieldDescriptor.TYPE_FLOAT: _Decoder.ReadFloat, + _FieldDescriptor.TYPE_INT64: _Decoder.ReadInt64, + _FieldDescriptor.TYPE_UINT64: _Decoder.ReadUInt64, + _FieldDescriptor.TYPE_INT32: _Decoder.ReadInt32, + _FieldDescriptor.TYPE_FIXED64: _Decoder.ReadFixed64, + _FieldDescriptor.TYPE_FIXED32: _Decoder.ReadFixed32, + _FieldDescriptor.TYPE_BOOL: _Decoder.ReadBool, + _FieldDescriptor.TYPE_STRING: _Decoder.ReadString, + _FieldDescriptor.TYPE_BYTES: _Decoder.ReadBytes, + _FieldDescriptor.TYPE_UINT32: _Decoder.ReadUInt32, + _FieldDescriptor.TYPE_ENUM: _Decoder.ReadEnum, + _FieldDescriptor.TYPE_SFIXED32: _Decoder.ReadSFixed32, + _FieldDescriptor.TYPE_SFIXED64: _Decoder.ReadSFixed64, + _FieldDescriptor.TYPE_SINT32: _Decoder.ReadSInt32, + _FieldDescriptor.TYPE_SINT64: _Decoder.ReadSInt64, + } diff --git a/python/google/protobuf/service.py b/python/google/protobuf/service.py new file mode 100755 index 00000000..461031b7 --- /dev/null +++ b/python/google/protobuf/service.py @@ -0,0 +1,194 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Declares the RPC service interfaces. + +This module declares the abstract interfaces underlying proto2 RPC +services. These are intented to be independent of any particular RPC +implementation, so that proto2 services can be used on top of a variety +of implementations. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + + +class Service(object): + + """Abstract base interface for protocol-buffer-based RPC services. + + Services themselves are abstract classes (implemented either by servers or as + stubs), but they subclass this base interface. The methods of this + interface can be used to call the methods of the service without knowing + its exact type at compile time (analogous to the Message interface). + """ + + def GetDescriptor(self): + """Retrieves this service's descriptor.""" + raise NotImplementedError + + def CallMethod(self, method_descriptor, rpc_controller, + request, done): + """Calls a method of the service specified by method_descriptor. + + Preconditions: + * method_descriptor.service == GetDescriptor + * request is of the exact same classes as returned by + GetRequestClass(method). + * After the call has started, the request must not be modified. + * "rpc_controller" is of the correct type for the RPC implementation being + used by this Service. For stubs, the "correct type" depends on the + RpcChannel which the stub is using. + + Postconditions: + * "done" will be called when the method is complete. This may be + before CallMethod() returns or it may be at some point in the future. + """ + raise NotImplementedError + + def GetRequestClass(self, method_descriptor): + """Returns the class of the request message for the specified method. + + CallMethod() requires that the request is of a particular subclass of + Message. GetRequestClass() gets the default instance of this required + type. + + Example: + method = service.GetDescriptor().FindMethodByName("Foo") + request = stub.GetRequestClass(method)() + request.ParseFromString(input) + service.CallMethod(method, request, callback) + """ + raise NotImplementedError + + def GetResponseClass(self, method_descriptor): + """Returns the class of the response message for the specified method. + + This method isn't really needed, as the RpcChannel's CallMethod constructs + the response protocol message. It's provided anyway in case it is useful + for the caller to know the response type in advance. + """ + raise NotImplementedError + + +class RpcController(object): + + """Abstract interface for an RPC channel. + + An RpcChannel represents a communication line to a service which can be used + to call that service's methods. The service may be running on another + machine. Normally, you should not use an RpcChannel directly, but instead + construct a stub {@link Service} wrapping it. Example: + + Example: + RpcChannel channel = rpcImpl.Channel("remotehost.example.com:1234") + RpcController controller = rpcImpl.Controller() + MyService service = MyService_Stub(channel) + service.MyMethod(controller, request, callback) + """ + + # Client-side methods below + + def Reset(self): + """Resets the RpcController to its initial state. + + After the RpcController has been reset, it may be reused in + a new call. Must not be called while an RPC is in progress. + """ + raise NotImplementedError + + def Failed(self): + """Returns true if the call failed. + + After a call has finished, returns true if the call failed. The possible + reasons for failure depend on the RPC implementation. Failed() must not + be called before a call has finished. If Failed() returns true, the + contents of the response message are undefined. + """ + raise NotImplementedError + + def ErrorText(self): + """If Failed is true, returns a human-readable description of the error.""" + raise NotImplementedError + + def StartCancel(self): + """Initiate cancellation. + + Advises the RPC system that the caller desires that the RPC call be + canceled. The RPC system may cancel it immediately, may wait awhile and + then cancel it, or may not even cancel the call at all. If the call is + canceled, the "done" callback will still be called and the RpcController + will indicate that the call failed at that time. + """ + raise NotImplementedError + + # Server-side methods below + + def SetFailed(self, reason): + """Sets a failure reason. + + Causes Failed() to return true on the client side. "reason" will be + incorporated into the message returned by ErrorText(). If you find + you need to return machine-readable information about failures, you + should incorporate it into your response protocol buffer and should + NOT call SetFailed(). + """ + raise NotImplementedError + + def IsCanceled(self): + """Checks if the client cancelled the RPC. + + If true, indicates that the client canceled the RPC, so the server may + as well give up on replying to it. The server should still call the + final "done" callback. + """ + raise NotImplementedError + + def NotifyOnCancel(self, callback): + """Sets a callback to invoke on cancel. + + Asks that the given callback be called when the RPC is canceled. The + callback will always be called exactly once. If the RPC completes without + being canceled, the callback will be called after completion. If the RPC + has already been canceled when NotifyOnCancel() is called, the callback + will be called immediately. + + NotifyOnCancel() must be called no more than once per request. + """ + raise NotImplementedError + + +class RpcChannel(object): + + """An RpcController mediates a single method call. + + The primary purpose of the controller is to provide a way to manipulate + settings specific to the RPC implementation and to find out about RPC-level + errors. The methods provided by the RpcController interface are intended + to be a "least common denominator" set of features which we expect all + implementations to support. Specific implementations may provide more + advanced features (e.g. deadline propagation). + """ + + def CallMethod(self, method_descriptor, rpc_controller, + request, response_class, done): + """Calls the method identified by the descriptor. + + Call the given method of the remote service. The signature of this + procedure looks the same as Service.CallMethod(), but the requirements + are less strict in one important way: the request object doesn't have to + be of any specific class as long as its descriptor is method.input_type. + """ + raise NotImplementedError diff --git a/python/google/protobuf/service_reflection.py b/python/google/protobuf/service_reflection.py new file mode 100755 index 00000000..6e3bf14e --- /dev/null +++ b/python/google/protobuf/service_reflection.py @@ -0,0 +1,275 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Contains metaclasses used to create protocol service and service stub +classes from ServiceDescriptor objects at runtime. + +The GeneratedServiceType and GeneratedServiceStubType metaclasses are used to +inject all useful functionality into the classes output by the protocol +compiler at compile-time. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + + +class GeneratedServiceType(type): + + """Metaclass for service classes created at runtime from ServiceDescriptors. + + Implementations for all methods described in the Service class are added here + by this class. We also create properties to allow getting/setting all fields + in the protocol message. + + The protocol compiler currently uses this metaclass to create protocol service + classes at runtime. Clients can also manually create their own classes at + runtime, as in this example: + + mydescriptor = ServiceDescriptor(.....) + class MyProtoService(service.Service): + __metaclass__ = GeneratedServiceType + DESCRIPTOR = mydescriptor + myservice_instance = MyProtoService() + ... + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service class. + + Args: + name: Name of the class (ignored, but required by the metaclass + protocol). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service class is subclassed. + if GeneratedServiceType._DESCRIPTOR_KEY not in dictionary: + return + descriptor = dictionary[GeneratedServiceType._DESCRIPTOR_KEY] + service_builder = _ServiceBuilder(descriptor) + service_builder.BuildService(cls) + + +class GeneratedServiceStubType(GeneratedServiceType): + + """Metaclass for service stubs created at runtime from ServiceDescriptors. + + This class has similar responsibilities as GeneratedServiceType, except that + it creates the service stub classes. + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service stub class. + + Args: + name: Name of the class (ignored, here). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + super(GeneratedServiceStubType, cls).__init__(name, bases, dictionary) + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service stub is subclassed. + if GeneratedServiceStubType._DESCRIPTOR_KEY not in dictionary: + return + descriptor = dictionary[GeneratedServiceStubType._DESCRIPTOR_KEY] + service_stub_builder = _ServiceStubBuilder(descriptor) + service_stub_builder.BuildServiceStub(cls) + + +class _ServiceBuilder(object): + + """This class constructs a protocol service class using a service descriptor. + + Given a service descriptor, this class constructs a class that represents + the specified service descriptor. One service builder instance constructs + exactly one service class. That means all instances of that class share the + same builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the + service class. + """ + self.descriptor = service_descriptor + + def BuildService(self, cls): + """Constructs the service class. + + Args: + cls: The class that will be constructed. + """ + + # CallMethod needs to operate with an instance of the Service class. This + # internal wrapper function exists only to be able to pass the service + # instance to the method that does the real CallMethod work. + def _WrapCallMethod(srvc, method_descriptor, + rpc_controller, request, callback): + self._CallMethod(srvc, method_descriptor, + rpc_controller, request, callback) + self.cls = cls + cls.CallMethod = _WrapCallMethod + cls.GetDescriptor = self._GetDescriptor + cls.GetRequestClass = self._GetRequestClass + cls.GetResponseClass = self._GetResponseClass + for method in self.descriptor.methods: + setattr(cls, method.name, self._GenerateNonImplementedMethod(method)) + + def _GetDescriptor(self): + """Retrieves the service descriptor. + + Returns: + The descriptor of the service (of type ServiceDescriptor). + """ + return self.descriptor + + def _CallMethod(self, srvc, method_descriptor, + rpc_controller, request, callback): + """Calls the method described by a given method descriptor. + + Args: + srvc: Instance of the service for which this method is called. + method_descriptor: Descriptor that represent the method to call. + rpc_controller: RPC controller to use for this method's execution. + request: Request protocol message. + callback: A callback to invoke after the method has completed. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'CallMethod() given method descriptor for wrong service type.') + method = getattr(self.cls, method_descriptor.name) + method(srvc, rpc_controller, request, callback) + + def _GetRequestClass(self, method_descriptor): + """Returns the class of the request protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + request protocol message class. + + Returns: + A class that represents the input protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetRequestClass() given method descriptor for wrong service type.') + return method_descriptor.input_type._concrete_class + + def _GetResponseClass(self, method_descriptor): + """Returns the class of the response protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + response protocol message class. + + Returns: + A class that represents the output protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetResponseClass() given method descriptor for wrong service type.') + return method_descriptor.output_type._concrete_class + + def _GenerateNonImplementedMethod(self, method): + """Generates and returns a method that can be set for a service methods. + + Args: + method: Descriptor of the service method for which a method is to be + generated. + + Returns: + A method that can be added to the service class. + """ + return lambda inst, rpc_controller, request, callback: ( + self._NonImplementedMethod(method.name, rpc_controller, callback)) + + def _NonImplementedMethod(self, method_name, rpc_controller, callback): + """The body of all methods in the generated service class. + + Args: + method_name: Name of the method being executed. + rpc_controller: RPC controller used to execute this method. + callback: A callback which will be invoked when the method finishes. + """ + rpc_controller.SetFailed('Method %s not implemented.' % method_name) + callback(None) + + +class _ServiceStubBuilder(object): + + """Constructs a protocol service stub class using a service descriptor. + + Given a service descriptor, this class constructs a suitable stub class. + A stub is just a type-safe wrapper around an RpcChannel which emulates a + local implementation of the service. + + One service stub builder instance constructs exactly one class. It means all + instances of that class share the same service stub builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service stub class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the + stub class. + """ + self.descriptor = service_descriptor + + def BuildServiceStub(self, cls): + """Constructs the stub class. + + Args: + cls: The class that will be constructed. + """ + + def _ServiceStubInit(stub, rpc_channel): + stub.rpc_channel = rpc_channel + self.cls = cls + cls.__init__ = _ServiceStubInit + for method in self.descriptor.methods: + setattr(cls, method.name, self._GenerateStubMethod(method)) + + def _GenerateStubMethod(self, method): + return lambda inst, rpc_controller, request, callback: self._StubMethod( + inst, method, rpc_controller, request, callback) + + def _StubMethod(self, stub, method_descriptor, + rpc_controller, request, callback): + """The body of all service methods in the generated stub class. + + Args: + stub: Stub instance. + method_descriptor: Descriptor of the invoked method. + rpc_controller: Rpc controller to execute the method. + request: Request protocol message. + callback: A callback to execute when the method finishes. + """ + stub.rpc_channel.CallMethod( + method_descriptor, rpc_controller, request, + method_descriptor.output_type._concrete_class, callback) diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py new file mode 100755 index 00000000..428b4c0a --- /dev/null +++ b/python/google/protobuf/text_format.py @@ -0,0 +1,111 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Contains routines for printing protocol messages in text format.""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +import cStringIO + +from google.protobuf import descriptor + +__all__ = [ 'MessageToString', 'PrintMessage', 'PrintField', 'PrintFieldValue' ] + +def MessageToString(message): + out = cStringIO.StringIO() + PrintMessage(message, out) + result = out.getvalue() + out.close() + return result + +def PrintMessage(message, out, indent = 0): + for field, value in message.ListFields(): + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + for element in value: + PrintField(field, element, out, indent) + else: + PrintField(field, value, out, indent) + +def PrintField(field, value, out, indent = 0): + """Print a single field name/value pair. For repeated fields, the value + should be a single element.""" + + out.write(' ' * indent); + if field.is_extension: + out.write('[') + if (field.containing_type.GetOptions().message_set_wire_format and + field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.message_type == field.extension_scope and + field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): + out.write(field.message_type.full_name) + else: + out.write(field.full_name) + out.write(']') + elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: + # For groups, use the capitalized name. + out.write(field.message_type.name) + else: + out.write(field.name) + + if field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + # The colon is optional in this case, but our cross-language golden files + # don't include it. + out.write(': ') + + PrintFieldValue(field, value, out, indent) + out.write('\n') + +def PrintFieldValue(field, value, out, indent = 0): + """Print a single field value (not including name). For repeated fields, + the value should be a single element.""" + + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + out.write(' {\n') + PrintMessage(value, out, indent + 2) + out.write(' ' * indent + '}') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + out.write(field.enum_type.values_by_number[value].name) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + out.write('\"') + out.write(_CEscape(value)) + out.write('\"') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + if value: + out.write("true") + else: + out.write("false") + else: + out.write(str(value)) + +# text.encode('string_escape') does not seem to satisfy our needs as it +# encodes unprintable characters using two-digit hex escapes whereas our +# C++ unescaping function allows hex escapes to be any length. So, +# "\0011".encode('string_escape') ends up being "\\x011", which will be +# decoded in C++ as a single-character string with char code 0x11. +def _CEscape(text): + def escape(c): + o = ord(c) + if o == 10: return r"\n" # optional escape + if o == 13: return r"\r" # optional escape + if o == 9: return r"\t" # optional escape + if o == 39: return r"\'" # optional escape + + if o == 34: return r'\"' # necessary escape + if o == 92: return r"\\" # necessary escape + + if o >= 127 or o < 32: return "\\%03o" % o # necessary escapes + return c + return "".join([escape(c) for c in text]) diff --git a/python/mox.py b/python/mox.py new file mode 100755 index 00000000..ce80ba50 --- /dev/null +++ b/python/mox.py @@ -0,0 +1,1401 @@ +#!/usr/bin/python2.4 +# +# Copyright 2008 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file is used for testing. The original is at: +# http://code.google.com/p/pymox/ + +"""Mox, an object-mocking framework for Python. + +Mox works in the record-replay-verify paradigm. When you first create +a mock object, it is in record mode. You then programmatically set +the expected behavior of the mock object (what methods are to be +called on it, with what parameters, what they should return, and in +what order). + +Once you have set up the expected mock behavior, you put it in replay +mode. Now the mock responds to method calls just as you told it to. +If an unexpected method (or an expected method with unexpected +parameters) is called, then an exception will be raised. + +Once you are done interacting with the mock, you need to verify that +all the expected interactions occured. (Maybe your code exited +prematurely without calling some cleanup method!) The verify phase +ensures that every expected method was called; otherwise, an exception +will be raised. + +Suggested usage / workflow: + + # Create Mox factory + my_mox = Mox() + + # Create a mock data access object + mock_dao = my_mox.CreateMock(DAOClass) + + # Set up expected behavior + mock_dao.RetrievePersonWithIdentifier('1').AndReturn(person) + mock_dao.DeletePerson(person) + + # Put mocks in replay mode + my_mox.ReplayAll() + + # Inject mock object and run test + controller.SetDao(mock_dao) + controller.DeletePersonById('1') + + # Verify all methods were called as expected + my_mox.VerifyAll() +""" + +from collections import deque +import re +import types +import unittest + +import stubout + +class Error(AssertionError): + """Base exception for this module.""" + + pass + + +class ExpectedMethodCallsError(Error): + """Raised when Verify() is called before all expected methods have been called + """ + + def __init__(self, expected_methods): + """Init exception. + + Args: + # expected_methods: A sequence of MockMethod objects that should have been + # called. + expected_methods: [MockMethod] + + Raises: + ValueError: if expected_methods contains no methods. + """ + + if not expected_methods: + raise ValueError("There must be at least one expected method") + Error.__init__(self) + self._expected_methods = expected_methods + + def __str__(self): + calls = "\n".join(["%3d. %s" % (i, m) + for i, m in enumerate(self._expected_methods)]) + return "Verify: Expected methods never called:\n%s" % (calls,) + + +class UnexpectedMethodCallError(Error): + """Raised when an unexpected method is called. + + This can occur if a method is called with incorrect parameters, or out of the + specified order. + """ + + def __init__(self, unexpected_method, expected): + """Init exception. + + Args: + # unexpected_method: MockMethod that was called but was not at the head of + # the expected_method queue. + # expected: MockMethod or UnorderedGroup the method should have + # been in. + unexpected_method: MockMethod + expected: MockMethod or UnorderedGroup + """ + + Error.__init__(self) + self._unexpected_method = unexpected_method + self._expected = expected + + def __str__(self): + return "Unexpected method call: %s. Expecting: %s" % \ + (self._unexpected_method, self._expected) + + +class UnknownMethodCallError(Error): + """Raised if an unknown method is requested of the mock object.""" + + def __init__(self, unknown_method_name): + """Init exception. + + Args: + # unknown_method_name: Method call that is not part of the mocked class's + # public interface. + unknown_method_name: str + """ + + Error.__init__(self) + self._unknown_method_name = unknown_method_name + + def __str__(self): + return "Method called is not a member of the object: %s" % \ + self._unknown_method_name + + +class Mox(object): + """Mox: a factory for creating mock objects.""" + + # A list of types that should be stubbed out with MockObjects (as + # opposed to MockAnythings). + _USE_MOCK_OBJECT = [types.ClassType, types.InstanceType, types.ModuleType, + types.ObjectType, types.TypeType] + + def __init__(self): + """Initialize a new Mox.""" + + self._mock_objects = [] + self.stubs = stubout.StubOutForTesting() + + def CreateMock(self, class_to_mock): + """Create a new mock object. + + Args: + # class_to_mock: the class to be mocked + class_to_mock: class + + Returns: + MockObject that can be used as the class_to_mock would be. + """ + + new_mock = MockObject(class_to_mock) + self._mock_objects.append(new_mock) + return new_mock + + def CreateMockAnything(self): + """Create a mock that will accept any method calls. + + This does not enforce an interface. + """ + + new_mock = MockAnything() + self._mock_objects.append(new_mock) + return new_mock + + def ReplayAll(self): + """Set all mock objects to replay mode.""" + + for mock_obj in self._mock_objects: + mock_obj._Replay() + + + def VerifyAll(self): + """Call verify on all mock objects created.""" + + for mock_obj in self._mock_objects: + mock_obj._Verify() + + def ResetAll(self): + """Call reset on all mock objects. This does not unset stubs.""" + + for mock_obj in self._mock_objects: + mock_obj._Reset() + + def StubOutWithMock(self, obj, attr_name, use_mock_anything=False): + """Replace a method, attribute, etc. with a Mock. + + This will replace a class or module with a MockObject, and everything else + (method, function, etc) with a MockAnything. This can be overridden to + always use a MockAnything by setting use_mock_anything to True. + + Args: + obj: A Python object (class, module, instance, callable). + attr_name: str. The name of the attribute to replace with a mock. + use_mock_anything: bool. True if a MockAnything should be used regardless + of the type of attribute. + """ + + attr_to_replace = getattr(obj, attr_name) + if type(attr_to_replace) in self._USE_MOCK_OBJECT and not use_mock_anything: + stub = self.CreateMock(attr_to_replace) + else: + stub = self.CreateMockAnything() + + self.stubs.Set(obj, attr_name, stub) + + def UnsetStubs(self): + """Restore stubs to their original state.""" + + self.stubs.UnsetAll() + +def Replay(*args): + """Put mocks into Replay mode. + + Args: + # args is any number of mocks to put into replay mode. + """ + + for mock in args: + mock._Replay() + + +def Verify(*args): + """Verify mocks. + + Args: + # args is any number of mocks to be verified. + """ + + for mock in args: + mock._Verify() + + +def Reset(*args): + """Reset mocks. + + Args: + # args is any number of mocks to be reset. + """ + + for mock in args: + mock._Reset() + + +class MockAnything: + """A mock that can be used to mock anything. + + This is helpful for mocking classes that do not provide a public interface. + """ + + def __init__(self): + """ """ + self._Reset() + + def __getattr__(self, method_name): + """Intercept method calls on this object. + + A new MockMethod is returned that is aware of the MockAnything's + state (record or replay). The call will be recorded or replayed + by the MockMethod's __call__. + + Args: + # method name: the name of the method being called. + method_name: str + + Returns: + A new MockMethod aware of MockAnything's state (record or replay). + """ + + return self._CreateMockMethod(method_name) + + def _CreateMockMethod(self, method_name): + """Create a new mock method call and return it. + + Args: + # method name: the name of the method being called. + method_name: str + + Returns: + A new MockMethod aware of MockAnything's state (record or replay). + """ + + return MockMethod(method_name, self._expected_calls_queue, + self._replay_mode) + + def __nonzero__(self): + """Return 1 for nonzero so the mock can be used as a conditional.""" + + return 1 + + def __eq__(self, rhs): + """Provide custom logic to compare objects.""" + + return (isinstance(rhs, MockAnything) and + self._replay_mode == rhs._replay_mode and + self._expected_calls_queue == rhs._expected_calls_queue) + + def __ne__(self, rhs): + """Provide custom logic to compare objects.""" + + return not self == rhs + + def _Replay(self): + """Start replaying expected method calls.""" + + self._replay_mode = True + + def _Verify(self): + """Verify that all of the expected calls have been made. + + Raises: + ExpectedMethodCallsError: if there are still more method calls in the + expected queue. + """ + + # If the list of expected calls is not empty, raise an exception + if self._expected_calls_queue: + # The last MultipleTimesGroup is not popped from the queue. + if (len(self._expected_calls_queue) == 1 and + isinstance(self._expected_calls_queue[0], MultipleTimesGroup) and + self._expected_calls_queue[0].IsSatisfied()): + pass + else: + raise ExpectedMethodCallsError(self._expected_calls_queue) + + def _Reset(self): + """Reset the state of this mock to record mode with an empty queue.""" + + # Maintain a list of method calls we are expecting + self._expected_calls_queue = deque() + + # Make sure we are in setup mode, not replay mode + self._replay_mode = False + + +class MockObject(MockAnything, object): + """A mock object that simulates the public/protected interface of a class.""" + + def __init__(self, class_to_mock): + """Initialize a mock object. + + This determines the methods and properties of the class and stores them. + + Args: + # class_to_mock: class to be mocked + class_to_mock: class + """ + + # This is used to hack around the mixin/inheritance of MockAnything, which + # is not a proper object (it can be anything. :-) + MockAnything.__dict__['__init__'](self) + + # Get a list of all the public and special methods we should mock. + self._known_methods = set() + self._known_vars = set() + self._class_to_mock = class_to_mock + for method in dir(class_to_mock): + if callable(getattr(class_to_mock, method)): + self._known_methods.add(method) + else: + self._known_vars.add(method) + + def __getattr__(self, name): + """Intercept attribute request on this object. + + If the attribute is a public class variable, it will be returned and not + recorded as a call. + + If the attribute is not a variable, it is handled like a method + call. The method name is checked against the set of mockable + methods, and a new MockMethod is returned that is aware of the + MockObject's state (record or replay). The call will be recorded + or replayed by the MockMethod's __call__. + + Args: + # name: the name of the attribute being requested. + name: str + + Returns: + Either a class variable or a new MockMethod that is aware of the state + of the mock (record or replay). + + Raises: + UnknownMethodCallError if the MockObject does not mock the requested + method. + """ + + if name in self._known_vars: + return getattr(self._class_to_mock, name) + + if name in self._known_methods: + return self._CreateMockMethod(name) + + raise UnknownMethodCallError(name) + + def __eq__(self, rhs): + """Provide custom logic to compare objects.""" + + return (isinstance(rhs, MockObject) and + self._class_to_mock == rhs._class_to_mock and + self._replay_mode == rhs._replay_mode and + self._expected_calls_queue == rhs._expected_calls_queue) + + def __setitem__(self, key, value): + """Provide custom logic for mocking classes that support item assignment. + + Args: + key: Key to set the value for. + value: Value to set. + + Returns: + Expected return value in replay mode. A MockMethod object for the + __setitem__ method that has already been called if not in replay mode. + + Raises: + TypeError if the underlying class does not support item assignment. + UnexpectedMethodCallError if the object does not expect the call to + __setitem__. + + """ + setitem = self._class_to_mock.__dict__.get('__setitem__', None) + + # Verify the class supports item assignment. + if setitem is None: + raise TypeError('object does not support item assignment') + + # If we are in replay mode then simply call the mock __setitem__ method. + if self._replay_mode: + return MockMethod('__setitem__', self._expected_calls_queue, + self._replay_mode)(key, value) + + + # Otherwise, create a mock method __setitem__. + return self._CreateMockMethod('__setitem__')(key, value) + + def __getitem__(self, key): + """Provide custom logic for mocking classes that are subscriptable. + + Args: + key: Key to return the value for. + + Returns: + Expected return value in replay mode. A MockMethod object for the + __getitem__ method that has already been called if not in replay mode. + + Raises: + TypeError if the underlying class is not subscriptable. + UnexpectedMethodCallError if the object does not expect the call to + __setitem__. + + """ + getitem = self._class_to_mock.__dict__.get('__getitem__', None) + + # Verify the class supports item assignment. + if getitem is None: + raise TypeError('unsubscriptable object') + + # If we are in replay mode then simply call the mock __getitem__ method. + if self._replay_mode: + return MockMethod('__getitem__', self._expected_calls_queue, + self._replay_mode)(key) + + + # Otherwise, create a mock method __getitem__. + return self._CreateMockMethod('__getitem__')(key) + + def __call__(self, *params, **named_params): + """Provide custom logic for mocking classes that are callable.""" + + # Verify the class we are mocking is callable + callable = self._class_to_mock.__dict__.get('__call__', None) + if callable is None: + raise TypeError('Not callable') + + # Because the call is happening directly on this object instead of a method, + # the call on the mock method is made right here + mock_method = self._CreateMockMethod('__call__') + return mock_method(*params, **named_params) + + @property + def __class__(self): + """Return the class that is being mocked.""" + + return self._class_to_mock + + +class MockMethod(object): + """Callable mock method. + + A MockMethod should act exactly like the method it mocks, accepting parameters + and returning a value, or throwing an exception (as specified). When this + method is called, it can optionally verify whether the called method (name and + signature) matches the expected method. + """ + + def __init__(self, method_name, call_queue, replay_mode): + """Construct a new mock method. + + Args: + # method_name: the name of the method + # call_queue: deque of calls, verify this call against the head, or add + # this call to the queue. + # replay_mode: False if we are recording, True if we are verifying calls + # against the call queue. + method_name: str + call_queue: list or deque + replay_mode: bool + """ + + self._name = method_name + self._call_queue = call_queue + if not isinstance(call_queue, deque): + self._call_queue = deque(self._call_queue) + self._replay_mode = replay_mode + + self._params = None + self._named_params = None + self._return_value = None + self._exception = None + self._side_effects = None + + def __call__(self, *params, **named_params): + """Log parameters and return the specified return value. + + If the Mock(Anything/Object) associated with this call is in record mode, + this MockMethod will be pushed onto the expected call queue. If the mock + is in replay mode, this will pop a MockMethod off the top of the queue and + verify this call is equal to the expected call. + + Raises: + UnexpectedMethodCall if this call is supposed to match an expected method + call and it does not. + """ + + self._params = params + self._named_params = named_params + + if not self._replay_mode: + self._call_queue.append(self) + return self + + expected_method = self._VerifyMethodCall() + + if expected_method._side_effects: + expected_method._side_effects(*params, **named_params) + + if expected_method._exception: + raise expected_method._exception + + return expected_method._return_value + + def __getattr__(self, name): + """Raise an AttributeError with a helpful message.""" + + raise AttributeError('MockMethod has no attribute "%s". ' + 'Did you remember to put your mocks in replay mode?' % name) + + def _PopNextMethod(self): + """Pop the next method from our call queue.""" + try: + return self._call_queue.popleft() + except IndexError: + raise UnexpectedMethodCallError(self, None) + + def _VerifyMethodCall(self): + """Verify the called method is expected. + + This can be an ordered method, or part of an unordered set. + + Returns: + The expected mock method. + + Raises: + UnexpectedMethodCall if the method called was not expected. + """ + + expected = self._PopNextMethod() + + # Loop here, because we might have a MethodGroup followed by another + # group. + while isinstance(expected, MethodGroup): + expected, method = expected.MethodCalled(self) + if method is not None: + return method + + # This is a mock method, so just check equality. + if expected != self: + raise UnexpectedMethodCallError(self, expected) + + return expected + + def __str__(self): + params = ', '.join( + [repr(p) for p in self._params or []] + + ['%s=%r' % x for x in sorted((self._named_params or {}).items())]) + desc = "%s(%s) -> %r" % (self._name, params, self._return_value) + return desc + + def __eq__(self, rhs): + """Test whether this MockMethod is equivalent to another MockMethod. + + Args: + # rhs: the right hand side of the test + rhs: MockMethod + """ + + return (isinstance(rhs, MockMethod) and + self._name == rhs._name and + self._params == rhs._params and + self._named_params == rhs._named_params) + + def __ne__(self, rhs): + """Test whether this MockMethod is not equivalent to another MockMethod. + + Args: + # rhs: the right hand side of the test + rhs: MockMethod + """ + + return not self == rhs + + def GetPossibleGroup(self): + """Returns a possible group from the end of the call queue or None if no + other methods are on the stack. + """ + + # Remove this method from the tail of the queue so we can add it to a group. + this_method = self._call_queue.pop() + assert this_method == self + + # Determine if the tail of the queue is a group, or just a regular ordered + # mock method. + group = None + try: + group = self._call_queue[-1] + except IndexError: + pass + + return group + + def _CheckAndCreateNewGroup(self, group_name, group_class): + """Checks if the last method (a possible group) is an instance of our + group_class. Adds the current method to this group or creates a new one. + + Args: + + group_name: the name of the group. + group_class: the class used to create instance of this new group + """ + group = self.GetPossibleGroup() + + # If this is a group, and it is the correct group, add the method. + if isinstance(group, group_class) and group.group_name() == group_name: + group.AddMethod(self) + return self + + # Create a new group and add the method. + new_group = group_class(group_name) + new_group.AddMethod(self) + self._call_queue.append(new_group) + return self + + def InAnyOrder(self, group_name="default"): + """Move this method into a group of unordered calls. + + A group of unordered calls must be defined together, and must be executed + in full before the next expected method can be called. There can be + multiple groups that are expected serially, if they are given + different group names. The same group name can be reused if there is a + standard method call, or a group with a different name, spliced between + usages. + + Args: + group_name: the name of the unordered group. + + Returns: + self + """ + return self._CheckAndCreateNewGroup(group_name, UnorderedGroup) + + def MultipleTimes(self, group_name="default"): + """Move this method into group of calls which may be called multiple times. + + A group of repeating calls must be defined together, and must be executed in + full before the next expected mehtod can be called. + + Args: + group_name: the name of the unordered group. + + Returns: + self + """ + return self._CheckAndCreateNewGroup(group_name, MultipleTimesGroup) + + def AndReturn(self, return_value): + """Set the value to return when this method is called. + + Args: + # return_value can be anything. + """ + + self._return_value = return_value + return return_value + + def AndRaise(self, exception): + """Set the exception to raise when this method is called. + + Args: + # exception: the exception to raise when this method is called. + exception: Exception + """ + + self._exception = exception + + def WithSideEffects(self, side_effects): + """Set the side effects that are simulated when this method is called. + + Args: + side_effects: A callable which modifies the parameters or other relevant + state which a given test case depends on. + + Returns: + Self for chaining with AndReturn and AndRaise. + """ + self._side_effects = side_effects + return self + +class Comparator: + """Base class for all Mox comparators. + + A Comparator can be used as a parameter to a mocked method when the exact + value is not known. For example, the code you are testing might build up a + long SQL string that is passed to your mock DAO. You're only interested that + the IN clause contains the proper primary keys, so you can set your mock + up as follows: + + mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result) + + Now whatever query is passed in must contain the string 'IN (1, 2, 4, 5)'. + + A Comparator may replace one or more parameters, for example: + # return at most 10 rows + mock_dao.RunQuery(StrContains('SELECT'), 10) + + or + + # Return some non-deterministic number of rows + mock_dao.RunQuery(StrContains('SELECT'), IsA(int)) + """ + + def equals(self, rhs): + """Special equals method that all comparators must implement. + + Args: + rhs: any python object + """ + + raise NotImplementedError, 'method must be implemented by a subclass.' + + def __eq__(self, rhs): + return self.equals(rhs) + + def __ne__(self, rhs): + return not self.equals(rhs) + + +class IsA(Comparator): + """This class wraps a basic Python type or class. It is used to verify + that a parameter is of the given type or class. + + Example: + mock_dao.Connect(IsA(DbConnectInfo)) + """ + + def __init__(self, class_name): + """Initialize IsA + + Args: + class_name: basic python type or a class + """ + + self._class_name = class_name + + def equals(self, rhs): + """Check to see if the RHS is an instance of class_name. + + Args: + # rhs: the right hand side of the test + rhs: object + + Returns: + bool + """ + + try: + return isinstance(rhs, self._class_name) + except TypeError: + # Check raw types if there was a type error. This is helpful for + # things like cStringIO.StringIO. + return type(rhs) == type(self._class_name) + + def __repr__(self): + return str(self._class_name) + +class IsAlmost(Comparator): + """Comparison class used to check whether a parameter is nearly equal + to a given value. Generally useful for floating point numbers. + + Example mock_dao.SetTimeout((IsAlmost(3.9))) + """ + + def __init__(self, float_value, places=7): + """Initialize IsAlmost. + + Args: + float_value: The value for making the comparison. + places: The number of decimal places to round to. + """ + + self._float_value = float_value + self._places = places + + def equals(self, rhs): + """Check to see if RHS is almost equal to float_value + + Args: + rhs: the value to compare to float_value + + Returns: + bool + """ + + try: + return round(rhs-self._float_value, self._places) == 0 + except TypeError: + # This is probably because either float_value or rhs is not a number. + return False + + def __repr__(self): + return str(self._float_value) + +class StrContains(Comparator): + """Comparison class used to check whether a substring exists in a + string parameter. This can be useful in mocking a database with SQL + passed in as a string parameter, for example. + + Example: + mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result) + """ + + def __init__(self, search_string): + """Initialize. + + Args: + # search_string: the string you are searching for + search_string: str + """ + + self._search_string = search_string + + def equals(self, rhs): + """Check to see if the search_string is contained in the rhs string. + + Args: + # rhs: the right hand side of the test + rhs: object + + Returns: + bool + """ + + try: + return rhs.find(self._search_string) > -1 + except Exception: + return False + + def __repr__(self): + return '' % self._search_string + + +class Regex(Comparator): + """Checks if a string matches a regular expression. + + This uses a given regular expression to determine equality. + """ + + def __init__(self, pattern, flags=0): + """Initialize. + + Args: + # pattern is the regular expression to search for + pattern: str + # flags passed to re.compile function as the second argument + flags: int + """ + + self.regex = re.compile(pattern, flags=flags) + + def equals(self, rhs): + """Check to see if rhs matches regular expression pattern. + + Returns: + bool + """ + + return self.regex.search(rhs) is not None + + def __repr__(self): + s = '' % self._key + + +class ContainsKeyValue(Comparator): + """Checks whether a key/value pair is in a dict parameter. + + Example: + mock_dao.UpdateUsers(ContainsKeyValue('stevepm', stevepm_user_info)) + """ + + def __init__(self, key, value): + """Initialize. + + Args: + # key: a key in a dict + # value: the corresponding value + """ + + self._key = key + self._value = value + + def equals(self, rhs): + """Check whether the given key/value pair is in the rhs dict. + + Returns: + bool + """ + + try: + return rhs[self._key] == self._value + except Exception: + return False + + def __repr__(self): + return '' % (self._key, self._value) + + +class SameElementsAs(Comparator): + """Checks whether iterables contain the same elements (ignoring order). + + Example: + mock_dao.ProcessUsers(SameElementsAs('stevepm', 'salomaki')) + """ + + def __init__(self, expected_seq): + """Initialize. + + Args: + expected_seq: a sequence + """ + + self._expected_seq = expected_seq + + def equals(self, actual_seq): + """Check to see whether actual_seq has same elements as expected_seq. + + Args: + actual_seq: sequence + + Returns: + bool + """ + + try: + expected = dict([(element, None) for element in self._expected_seq]) + actual = dict([(element, None) for element in actual_seq]) + except TypeError: + # Fall back to slower list-compare if any of the objects are unhashable. + expected = list(self._expected_seq) + actual = list(actual_seq) + expected.sort() + actual.sort() + return expected == actual + + def __repr__(self): + return '' % self._expected_seq + + +class And(Comparator): + """Evaluates one or more Comparators on RHS and returns an AND of the results. + """ + + def __init__(self, *args): + """Initialize. + + Args: + *args: One or more Comparator + """ + + self._comparators = args + + def equals(self, rhs): + """Checks whether all Comparators are equal to rhs. + + Args: + # rhs: can be anything + + Returns: + bool + """ + + for comparator in self._comparators: + if not comparator.equals(rhs): + return False + + return True + + def __repr__(self): + return '' % str(self._comparators) + + +class Or(Comparator): + """Evaluates one or more Comparators on RHS and returns an OR of the results. + """ + + def __init__(self, *args): + """Initialize. + + Args: + *args: One or more Mox comparators + """ + + self._comparators = args + + def equals(self, rhs): + """Checks whether any Comparator is equal to rhs. + + Args: + # rhs: can be anything + + Returns: + bool + """ + + for comparator in self._comparators: + if comparator.equals(rhs): + return True + + return False + + def __repr__(self): + return '' % str(self._comparators) + + +class Func(Comparator): + """Call a function that should verify the parameter passed in is correct. + + You may need the ability to perform more advanced operations on the parameter + in order to validate it. You can use this to have a callable validate any + parameter. The callable should return either True or False. + + + Example: + + def myParamValidator(param): + # Advanced logic here + return True + + mock_dao.DoSomething(Func(myParamValidator), true) + """ + + def __init__(self, func): + """Initialize. + + Args: + func: callable that takes one parameter and returns a bool + """ + + self._func = func + + def equals(self, rhs): + """Test whether rhs passes the function test. + + rhs is passed into func. + + Args: + rhs: any python object + + Returns: + the result of func(rhs) + """ + + return self._func(rhs) + + def __repr__(self): + return str(self._func) + + +class IgnoreArg(Comparator): + """Ignore an argument. + + This can be used when we don't care about an argument of a method call. + + Example: + # Check if CastMagic is called with 3 as first arg and 'disappear' as third. + mymock.CastMagic(3, IgnoreArg(), 'disappear') + """ + + def equals(self, unused_rhs): + """Ignores arguments and returns True. + + Args: + unused_rhs: any python object + + Returns: + always returns True + """ + + return True + + def __repr__(self): + return '' + + +class MethodGroup(object): + """Base class containing common behaviour for MethodGroups.""" + + def __init__(self, group_name): + self._group_name = group_name + + def group_name(self): + return self._group_name + + def __str__(self): + return '<%s "%s">' % (self.__class__.__name__, self._group_name) + + def AddMethod(self, mock_method): + raise NotImplementedError + + def MethodCalled(self, mock_method): + raise NotImplementedError + + def IsSatisfied(self): + raise NotImplementedError + +class UnorderedGroup(MethodGroup): + """UnorderedGroup holds a set of method calls that may occur in any order. + + This construct is helpful for non-deterministic events, such as iterating + over the keys of a dict. + """ + + def __init__(self, group_name): + super(UnorderedGroup, self).__init__(group_name) + self._methods = [] + + def AddMethod(self, mock_method): + """Add a method to this group. + + Args: + mock_method: A mock method to be added to this group. + """ + + self._methods.append(mock_method) + + def MethodCalled(self, mock_method): + """Remove a method call from the group. + + If the method is not in the set, an UnexpectedMethodCallError will be + raised. + + Args: + mock_method: a mock method that should be equal to a method in the group. + + Returns: + The mock method from the group + + Raises: + UnexpectedMethodCallError if the mock_method was not in the group. + """ + + # Check to see if this method exists, and if so, remove it from the set + # and return it. + for method in self._methods: + if method == mock_method: + # Remove the called mock_method instead of the method in the group. + # The called method will match any comparators when equality is checked + # during removal. The method in the group could pass a comparator to + # another comparator during the equality check. + self._methods.remove(mock_method) + + # If this group is not empty, put it back at the head of the queue. + if not self.IsSatisfied(): + mock_method._call_queue.appendleft(self) + + return self, method + + raise UnexpectedMethodCallError(mock_method, self) + + def IsSatisfied(self): + """Return True if there are not any methods in this group.""" + + return len(self._methods) == 0 + + +class MultipleTimesGroup(MethodGroup): + """MultipleTimesGroup holds methods that may be called any number of times. + + Note: Each method must be called at least once. + + This is helpful, if you don't know or care how many times a method is called. + """ + + def __init__(self, group_name): + super(MultipleTimesGroup, self).__init__(group_name) + self._methods = set() + self._methods_called = set() + + def AddMethod(self, mock_method): + """Add a method to this group. + + Args: + mock_method: A mock method to be added to this group. + """ + + self._methods.add(mock_method) + + def MethodCalled(self, mock_method): + """Remove a method call from the group. + + If the method is not in the set, an UnexpectedMethodCallError will be + raised. + + Args: + mock_method: a mock method that should be equal to a method in the group. + + Returns: + The mock method from the group + + Raises: + UnexpectedMethodCallError if the mock_method was not in the group. + """ + + # Check to see if this method exists, and if so add it to the set of + # called methods. + + for method in self._methods: + if method == mock_method: + self._methods_called.add(mock_method) + # Always put this group back on top of the queue, because we don't know + # when we are done. + mock_method._call_queue.appendleft(self) + return self, method + + if self.IsSatisfied(): + next_method = mock_method._PopNextMethod(); + return next_method, None + else: + raise UnexpectedMethodCallError(mock_method, self) + + def IsSatisfied(self): + """Return True if all methods in this group are called at least once.""" + # NOTE(psycho): We can't use the simple set difference here because we want + # to match different parameters which are considered the same e.g. IsA(str) + # and some string. This solution is O(n^2) but n should be small. + tmp = self._methods.copy() + for called in self._methods_called: + for expected in tmp: + if called == expected: + tmp.remove(expected) + if not tmp: + return True + break + return False + + +class MoxMetaTestBase(type): + """Metaclass to add mox cleanup and verification to every test. + + As the mox unit testing class is being constructed (MoxTestBase or a + subclass), this metaclass will modify all test functions to call the + CleanUpMox method of the test class after they finish. This means that + unstubbing and verifying will happen for every test with no additional code, + and any failures will result in test failures as opposed to errors. + """ + + def __init__(cls, name, bases, d): + type.__init__(cls, name, bases, d) + + # also get all the attributes from the base classes to account + # for a case when test class is not the immediate child of MoxTestBase + for base in bases: + for attr_name in dir(base): + d[attr_name] = getattr(base, attr_name) + + for func_name, func in d.items(): + if func_name.startswith('test') and callable(func): + setattr(cls, func_name, MoxMetaTestBase.CleanUpTest(cls, func)) + + @staticmethod + def CleanUpTest(cls, func): + """Adds Mox cleanup code to any MoxTestBase method. + + Always unsets stubs after a test. Will verify all mocks for tests that + otherwise pass. + + Args: + cls: MoxTestBase or subclass; the class whose test method we are altering. + func: method; the method of the MoxTestBase test class we wish to alter. + + Returns: + The modified method. + """ + def new_method(self, *args, **kwargs): + mox_obj = getattr(self, 'mox', None) + cleanup_mox = False + if mox_obj and isinstance(mox_obj, Mox): + cleanup_mox = True + try: + func(self, *args, **kwargs) + finally: + if cleanup_mox: + mox_obj.UnsetStubs() + if cleanup_mox: + mox_obj.VerifyAll() + new_method.__name__ = func.__name__ + new_method.__doc__ = func.__doc__ + new_method.__module__ = func.__module__ + return new_method + + +class MoxTestBase(unittest.TestCase): + """Convenience test class to make stubbing easier. + + Sets up a "mox" attribute which is an instance of Mox - any mox tests will + want this. Also automatically unsets any stubs and verifies that all mock + methods have been called at the end of each test, eliminating boilerplate + code. + """ + + __metaclass__ = MoxMetaTestBase + + def setUp(self): + self.mox = Mox() diff --git a/python/setup.py b/python/setup.py new file mode 100755 index 00000000..eda25749 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,126 @@ +#! /usr/bin/python +# +# See README for usage instructions. + +# We must use setuptools, not distutils, because we need to use the +# namespace_packages option for the "google" package. +from ez_setup import use_setuptools +use_setuptools() + +from setuptools import setup +from distutils.spawn import find_executable +import sys +import os + +maintainer_email = "protobuf@googlegroups.com" + +# Find the Protocol Compiler. +if os.path.exists("../src/protoc"): + protoc = "../src/protoc" +else: + protoc = find_executable("protoc") + +def generate_proto(source): + """Invokes the Protocol Compiler to generate a _pb2.py from the given + .proto file. Does nothing if the output already exists and is newer than + the input.""" + + output = source.replace(".proto", "_pb2.py").replace("../src/", "") + + if not os.path.exists(source): + print "Can't find required file: " + source + sys.exit(-1) + + if (not os.path.exists(output) or + (os.path.exists(source) and + os.path.getmtime(source) > os.path.getmtime(output))): + print "Generating %s..." % output + + if protoc == None: + sys.stderr.write( + "protoc is not installed nor found in ../src. Please compile it " + "or install the binary package.\n") + sys.exit(-1) + + protoc_command = protoc + " -I../src -I. --python_out=. " + source + if os.system(protoc_command) != 0: + sys.exit(-1) + +def MakeTestSuite(): + generate_proto("../src/google/protobuf/unittest.proto") + generate_proto("../src/google/protobuf/unittest_import.proto") + generate_proto("../src/google/protobuf/unittest_mset.proto") + generate_proto("google/protobuf/internal/more_extensions.proto") + generate_proto("google/protobuf/internal/more_messages.proto") + + import unittest + import google.protobuf.internal.generator_test as generator_test + import google.protobuf.internal.decoder_test as decoder_test + import google.protobuf.internal.descriptor_test as descriptor_test + import google.protobuf.internal.encoder_test as encoder_test + import google.protobuf.internal.input_stream_test as input_stream_test + import google.protobuf.internal.output_stream_test as output_stream_test + import google.protobuf.internal.reflection_test as reflection_test + import google.protobuf.internal.service_reflection_test \ + as service_reflection_test + import google.protobuf.internal.text_format_test as text_format_test + import google.protobuf.internal.wire_format_test as wire_format_test + + loader = unittest.defaultTestLoader + suite = unittest.TestSuite() + for test in [ generator_test, + decoder_test, + descriptor_test, + encoder_test, + input_stream_test, + output_stream_test, + reflection_test, + service_reflection_test, + text_format_test, + wire_format_test ]: + suite.addTest(loader.loadTestsFromModule(test)) + + return suite + +if __name__ == '__main__': + # TODO(kenton): Integrate this into setuptools somehow? + if len(sys.argv) >= 2 and sys.argv[1] == "clean": + # Delete generated _pb2.py files and .pyc files in the code tree. + for (dirpath, dirnames, filenames) in os.walk("."): + for filename in filenames: + filepath = os.path.join(dirpath, filename) + if filepath.endswith("_pb2.py") or filepath.endswith(".pyc"): + os.remove(filepath) + else: + # Generate necessary .proto file if it doesn't exist. + # TODO(kenton): Maybe we should hook this into a distutils command? + generate_proto("../src/google/protobuf/descriptor.proto") + + setup(name = 'protobuf', + version = '2.0.1-SNAPSHOT', + packages = [ 'google' ], + namespace_packages = [ 'google' ], + test_suite = 'setup.MakeTestSuite', + # Must list modules explicitly so that we don't install tests. + py_modules = [ + 'google.protobuf.internal.decoder', + 'google.protobuf.internal.encoder', + 'google.protobuf.internal.input_stream', + 'google.protobuf.internal.message_listener', + 'google.protobuf.internal.output_stream', + 'google.protobuf.internal.wire_format', + 'google.protobuf.descriptor', + 'google.protobuf.descriptor_pb2', + 'google.protobuf.message', + 'google.protobuf.reflection', + 'google.protobuf.service', + 'google.protobuf.service_reflection', + 'google.protobuf.text_format'], + url = 'http://code.google.com/p/protobuf/', + maintainer = maintainer_email, + maintainer_email = 'protobuf@googlegroups.com', + license = 'Apache License, Version 2.0', + description = 'Protocol Buffers', + long_description = + "Protocol Buffers are Google's data interchange format.", + ) diff --git a/python/stubout.py b/python/stubout.py new file mode 100755 index 00000000..aee4f2da --- /dev/null +++ b/python/stubout.py @@ -0,0 +1,140 @@ +#!/usr/bin/python2.4 +# +# Copyright 2008 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file is used for testing. The original is at: +# http://code.google.com/p/pymox/ + +class StubOutForTesting: + """Sample Usage: + You want os.path.exists() to always return true during testing. + + stubs = StubOutForTesting() + stubs.Set(os.path, 'exists', lambda x: 1) + ... + stubs.UnsetAll() + + The above changes os.path.exists into a lambda that returns 1. Once + the ... part of the code finishes, the UnsetAll() looks up the old value + of os.path.exists and restores it. + + """ + def __init__(self): + self.cache = [] + self.stubs = [] + + def __del__(self): + self.SmartUnsetAll() + self.UnsetAll() + + def SmartSet(self, obj, attr_name, new_attr): + """Replace obj.attr_name with new_attr. This method is smart and works + at the module, class, and instance level while preserving proper + inheritance. It will not stub out C types however unless that has been + explicitly allowed by the type. + + This method supports the case where attr_name is a staticmethod or a + classmethod of obj. + + Notes: + - If obj is an instance, then it is its class that will actually be + stubbed. Note that the method Set() does not do that: if obj is + an instance, it (and not its class) will be stubbed. + - The stubbing is using the builtin getattr and setattr. So, the __get__ + and __set__ will be called when stubbing (TODO: A better idea would + probably be to manipulate obj.__dict__ instead of getattr() and + setattr()). + + Raises AttributeError if the attribute cannot be found. + """ + if (inspect.ismodule(obj) or + (not inspect.isclass(obj) and obj.__dict__.has_key(attr_name))): + orig_obj = obj + orig_attr = getattr(obj, attr_name) + + else: + if not inspect.isclass(obj): + mro = list(inspect.getmro(obj.__class__)) + else: + mro = list(inspect.getmro(obj)) + + mro.reverse() + + orig_attr = None + + for cls in mro: + try: + orig_obj = cls + orig_attr = getattr(obj, attr_name) + except AttributeError: + continue + + if orig_attr is None: + raise AttributeError("Attribute not found.") + + # Calling getattr() on a staticmethod transforms it to a 'normal' function. + # We need to ensure that we put it back as a staticmethod. + old_attribute = obj.__dict__.get(attr_name) + if old_attribute is not None and isinstance(old_attribute, staticmethod): + orig_attr = staticmethod(orig_attr) + + self.stubs.append((orig_obj, attr_name, orig_attr)) + setattr(orig_obj, attr_name, new_attr) + + def SmartUnsetAll(self): + """Reverses all the SmartSet() calls, restoring things to their original + definition. Its okay to call SmartUnsetAll() repeatedly, as later calls + have no effect if no SmartSet() calls have been made. + + """ + self.stubs.reverse() + + for args in self.stubs: + setattr(*args) + + self.stubs = [] + + def Set(self, parent, child_name, new_child): + """Replace child_name's old definition with new_child, in the context + of the given parent. The parent could be a module when the child is a + function at module scope. Or the parent could be a class when a class' + method is being replaced. The named child is set to new_child, while + the prior definition is saved away for later, when UnsetAll() is called. + + This method supports the case where child_name is a staticmethod or a + classmethod of parent. + """ + old_child = getattr(parent, child_name) + + old_attribute = parent.__dict__.get(child_name) + if old_attribute is not None and isinstance(old_attribute, staticmethod): + old_child = staticmethod(old_child) + + self.cache.append((parent, old_child, child_name)) + setattr(parent, child_name, new_child) + + def UnsetAll(self): + """Reverses all the Set() calls, restoring things to their original + definition. Its okay to call UnsetAll() repeatedly, as later calls have + no effect if no Set() calls have been made. + + """ + # Undo calls to Set() in reverse order, in case Set() was called on the + # same arguments repeatedly (want the original call to be last one undone) + self.cache.reverse() + + for (parent, old_child, child_name) in self.cache: + setattr(parent, child_name, old_child) + self.cache = [] diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..2997cdd5 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,255 @@ +## Process this file with automake to produce Makefile.in + +if GCC +# These are good warnings to turn on by default +AM_CXXFLAGS = $(PTHREAD_CFLAGS) -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare +else +AM_CXXFLAGS = $(PTHREAD_CFLAGS) +endif + +AM_LDFLAGS = $(PTHREAD_CFLAGS) + +# If I say "dist_include_DATA", automake complains that $(includedir) is not +# a "legitimate" directory for DATA. Screw you, automake. +protodir = $(includedir) +nobase_dist_proto_DATA = google/protobuf/descriptor.proto + +# Not sure why these don't get cleaned automatically. +clean-local: + rm -f *.loT + +CLEANFILES = $(protoc_outputs) unittest_proto_middleman + +MAINTAINERCLEANFILES = \ + Makefile.in + +nobase_include_HEADERS = \ + google/protobuf/stubs/common.h \ + google/protobuf/descriptor.h \ + google/protobuf/descriptor.pb.h \ + google/protobuf/descriptor_database.h \ + google/protobuf/dynamic_message.h \ + google/protobuf/extension_set.h \ + google/protobuf/generated_message_reflection.h \ + google/protobuf/message.h \ + google/protobuf/reflection_ops.h \ + google/protobuf/repeated_field.h \ + google/protobuf/service.h \ + google/protobuf/text_format.h \ + google/protobuf/unknown_field_set.h \ + google/protobuf/wire_format.h \ + google/protobuf/wire_format_inl.h \ + google/protobuf/io/coded_stream.h \ + google/protobuf/io/printer.h \ + google/protobuf/io/tokenizer.h \ + google/protobuf/io/zero_copy_stream.h \ + google/protobuf/io/zero_copy_stream_impl.h \ + google/protobuf/compiler/code_generator.h \ + google/protobuf/compiler/command_line_interface.h \ + google/protobuf/compiler/importer.h \ + google/protobuf/compiler/parser.h \ + google/protobuf/compiler/cpp/cpp_generator.h \ + google/protobuf/compiler/java/java_generator.h \ + google/protobuf/compiler/python/python_generator.h + +lib_LTLIBRARIES = libprotobuf.la libprotoc.la + +libprotobuf_la_LIBADD = $(PTHREAD_LIBS) +libprotobuf_la_LDFLAGS = -version-info 0:0:0 +libprotobuf_la_SOURCES = \ + google/protobuf/stubs/common.cc \ + google/protobuf/stubs/hash.cc \ + google/protobuf/stubs/hash.h \ + google/protobuf/stubs/map-util.cc \ + google/protobuf/stubs/map-util.h \ + google/protobuf/stubs/stl_util-inl.cc \ + google/protobuf/stubs/stl_util-inl.h \ + google/protobuf/stubs/substitute.cc \ + google/protobuf/stubs/substitute.h \ + google/protobuf/stubs/strutil.cc \ + google/protobuf/stubs/strutil.h \ + google/protobuf/descriptor.cc \ + google/protobuf/descriptor.pb.cc \ + google/protobuf/descriptor_database.cc \ + google/protobuf/dynamic_message.cc \ + google/protobuf/extension_set.cc \ + google/protobuf/generated_message_reflection.cc \ + google/protobuf/message.cc \ + google/protobuf/reflection_ops.cc \ + google/protobuf/repeated_field.cc \ + google/protobuf/service.cc \ + google/protobuf/text_format.cc \ + google/protobuf/unknown_field_set.cc \ + google/protobuf/wire_format.cc \ + google/protobuf/io/coded_stream.cc \ + google/protobuf/io/printer.cc \ + google/protobuf/io/tokenizer.cc \ + google/protobuf/io/zero_copy_stream.cc \ + google/protobuf/io/zero_copy_stream_impl.cc \ + google/protobuf/compiler/importer.cc \ + google/protobuf/compiler/parser.cc + +libprotoc_la_LIBADD = $(PTHREAD_LIBS) libprotobuf.la +libprotoc_la_LDFLAGS = -version-info 0:0:0 +libprotoc_la_SOURCES = \ + google/protobuf/compiler/code_generator.cc \ + google/protobuf/compiler/command_line_interface.cc \ + google/protobuf/compiler/cpp/cpp_enum.cc \ + google/protobuf/compiler/cpp/cpp_enum.h \ + google/protobuf/compiler/cpp/cpp_enum_field.cc \ + google/protobuf/compiler/cpp/cpp_enum_field.h \ + google/protobuf/compiler/cpp/cpp_extension.cc \ + google/protobuf/compiler/cpp/cpp_extension.h \ + google/protobuf/compiler/cpp/cpp_field.cc \ + google/protobuf/compiler/cpp/cpp_field.h \ + google/protobuf/compiler/cpp/cpp_file.cc \ + google/protobuf/compiler/cpp/cpp_file.h \ + google/protobuf/compiler/cpp/cpp_generator.cc \ + google/protobuf/compiler/cpp/cpp_helpers.cc \ + google/protobuf/compiler/cpp/cpp_helpers.h \ + google/protobuf/compiler/cpp/cpp_message.cc \ + google/protobuf/compiler/cpp/cpp_message.h \ + google/protobuf/compiler/cpp/cpp_message_field.cc \ + google/protobuf/compiler/cpp/cpp_message_field.h \ + google/protobuf/compiler/cpp/cpp_primitive_field.cc \ + google/protobuf/compiler/cpp/cpp_primitive_field.h \ + google/protobuf/compiler/cpp/cpp_service.cc \ + google/protobuf/compiler/cpp/cpp_service.h \ + google/protobuf/compiler/cpp/cpp_string_field.cc \ + google/protobuf/compiler/cpp/cpp_string_field.h \ + google/protobuf/compiler/java/java_enum.cc \ + google/protobuf/compiler/java/java_enum.h \ + google/protobuf/compiler/java/java_enum_field.cc \ + google/protobuf/compiler/java/java_enum_field.h \ + google/protobuf/compiler/java/java_extension.cc \ + google/protobuf/compiler/java/java_extension.h \ + google/protobuf/compiler/java/java_field.cc \ + google/protobuf/compiler/java/java_field.h \ + google/protobuf/compiler/java/java_file.cc \ + google/protobuf/compiler/java/java_file.h \ + google/protobuf/compiler/java/java_generator.cc \ + google/protobuf/compiler/java/java_helpers.cc \ + google/protobuf/compiler/java/java_helpers.h \ + google/protobuf/compiler/java/java_message.cc \ + google/protobuf/compiler/java/java_message.h \ + google/protobuf/compiler/java/java_message_field.cc \ + google/protobuf/compiler/java/java_message_field.h \ + google/protobuf/compiler/java/java_primitive_field.cc \ + google/protobuf/compiler/java/java_primitive_field.h \ + google/protobuf/compiler/java/java_service.cc \ + google/protobuf/compiler/java/java_service.h \ + google/protobuf/compiler/python/python_generator.cc + +bin_PROGRAMS = protoc +protoc_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la +protoc_SOURCES = google/protobuf/compiler/main.cc + +# Tests ============================================================== + +protoc_inputs = \ + google/protobuf/unittest.proto \ + google/protobuf/unittest_import.proto \ + google/protobuf/unittest_mset.proto \ + google/protobuf/unittest_optimize_for.proto \ + google/protobuf/unittest_embed_optimize_for.proto \ + google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto + +EXTRA_DIST = \ + $(protoc_inputs) \ + solaris/libstdc++.la \ + google/protobuf/testdata/golden_message \ + google/protobuf/testdata/text_format_unittest_data.txt \ + google/protobuf/testdata/text_format_unittest_extensions_data.txt \ + google/protobuf/package_info.h \ + google/protobuf/io/package_info.h \ + google/protobuf/compiler/package_info.h \ + gtest/CHANGES \ + gtest/CONTRIBUTORS \ + gtest/COPYING \ + gtest/README \ + gtest/gen_gtest_pred_impl.py + +protoc_outputs = \ + google/protobuf/unittest.pb.cc \ + google/protobuf/unittest.pb.h \ + google/protobuf/unittest_import.pb.cc \ + google/protobuf/unittest_import.pb.h \ + google/protobuf/unittest_mset.pb.cc \ + google/protobuf/unittest_mset.pb.h \ + google/protobuf/unittest_optimize_for.pb.cc \ + google/protobuf/unittest_optimize_for.pb.h \ + google/protobuf/unittest_embed_optimize_for.pb.cc \ + google/protobuf/unittest_embed_optimize_for.pb.h \ + google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.cc \ + google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h + +BUILT_SOURCES = $(protoc_outputs) + +# This rule is a little weird. The first prereq is the protoc executable +# and the rest are its inputs. Therefore, $^ -- which expands to the +# list of prereqs -- is actually a valid command. We have to place "./" in +# front of it in case protoc is in the current directory. protoc allows +# flags to appear after input file names, so we happily stick the flags on +# the end. +# +# For reference, if we didn't have to worry about VPATH (i.e., building from +# a directory other than the package root), we could have just written this: +# ./protoc$(EXEEXT) -I$(srcdir) --cpp_out=. $(protoc_inputs) +unittest_proto_middleman: protoc$(EXEEXT) $(protoc_inputs) + ./$^ -I$(srcdir) --cpp_out=. + touch unittest_proto_middleman + +$(protoc_outputs): unittest_proto_middleman + +noinst_PROGRAMS = protobuf-test +protobuf_test_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la +protobuf_test_SOURCES = \ + google/protobuf/stubs/common_unittest.cc \ + google/protobuf/stubs/strutil_unittest.cc \ + google/protobuf/descriptor_database_unittest.cc \ + google/protobuf/descriptor_unittest.cc \ + google/protobuf/dynamic_message_unittest.cc \ + google/protobuf/extension_set_unittest.cc \ + google/protobuf/generated_message_reflection_unittest.cc \ + google/protobuf/message_unittest.cc \ + google/protobuf/reflection_ops_unittest.cc \ + google/protobuf/repeated_field_unittest.cc \ + google/protobuf/text_format_unittest.cc \ + google/protobuf/unknown_field_set_unittest.cc \ + google/protobuf/wire_format_unittest.cc \ + google/protobuf/io/coded_stream_unittest.cc \ + google/protobuf/io/printer_unittest.cc \ + google/protobuf/io/tokenizer_unittest.cc \ + google/protobuf/io/zero_copy_stream_unittest.cc \ + google/protobuf/compiler/command_line_interface_unittest.cc \ + google/protobuf/compiler/importer_unittest.cc \ + google/protobuf/compiler/parser_unittest.cc \ + google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc \ + google/protobuf/compiler/cpp/cpp_unittest.cc \ + google/protobuf/test_util.cc \ + google/protobuf/test_util.h \ + google/protobuf/testing/googletest.cc \ + google/protobuf/testing/googletest.h \ + google/protobuf/testing/file.cc \ + google/protobuf/testing/file.h \ + gtest/gtest.cc \ + gtest/gtest.h \ + gtest/gtest-death-test.cc \ + gtest/gtest-death-test.h \ + gtest/gtest-filepath.cc \ + gtest/gtest-internal-inl.h \ + gtest/gtest-message.h \ + gtest/gtest-port.cc \ + gtest/gtest-spi.h \ + gtest/gtest_main.cc \ + gtest/gtest_pred_impl.h \ + gtest/gtest_prod.h \ + gtest/internal/gtest-death-test-internal.h \ + gtest/internal/gtest-filepath.h \ + gtest/internal/gtest-internal.h \ + gtest/internal/gtest-port.h \ + gtest/internal/gtest-string.h + +nodist_protobuf_test_SOURCES = $(protoc_outputs) + +TESTS = protobuf-test diff --git a/src/google/protobuf/compiler/code_generator.cc b/src/google/protobuf/compiler/code_generator.cc new file mode 100644 index 00000000..d3a051d0 --- /dev/null +++ b/src/google/protobuf/compiler/code_generator.cc @@ -0,0 +1,32 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +namespace google { +namespace protobuf { +namespace compiler { + +CodeGenerator::~CodeGenerator() {} +OutputDirectory::~OutputDirectory() {} + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h new file mode 100644 index 00000000..8f9938e3 --- /dev/null +++ b/src/google/protobuf/compiler/code_generator.h @@ -0,0 +1,98 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Defines the abstract interface implemented by each of the language-specific +// code generators. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__ + +#include +#include + +namespace google { +namespace protobuf { + +namespace io { class ZeroCopyOutputStream; } +class FileDescriptor; + +namespace compiler { + +// Defined in this file. +class CodeGenerator; +class OutputDirectory; + +// The abstract interface to a class which generates code implementing a +// particular proto file in a particular language. A number of these may +// be registered with CommandLineInterface to support various languages. +class LIBPROTOC_EXPORT CodeGenerator { + public: + inline CodeGenerator() {} + virtual ~CodeGenerator(); + + // Generates code for the given proto file, generating one or more files in + // the given output directory. + // + // A parameter to be passed to the generator can be specified on the + // command line. This is intended to be used by Java and similar languages + // to specify which specific class from the proto file is to be generated, + // though it could have other uses as well. It is empty if no parameter was + // given. + // + // Returns true if successful. Otherwise, sets *error to a description of + // the problem (e.g. "invalid parameter") and returns false. + virtual bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodeGenerator); +}; + +// CodeGenerators generate one or more files in a given directory. This +// abstract interface represents the directory to which the CodeGenerator is +// to write. +class LIBPROTOC_EXPORT OutputDirectory { + public: + inline OutputDirectory() {} + virtual ~OutputDirectory(); + + // Opens the given file, truncating it if it exists, and returns a + // ZeroCopyOutputStream that writes to the file. The caller takes ownership + // of the returned object. This method never fails (a dummy stream will be + // returned instead). + // + // The filename given should be relative to the root of the source tree. + // E.g. the C++ generator, when generating code for "foo/bar.proto", will + // generate the files "foo/bar.pb2.h" and "foo/bar.pb2.cc"; note that + // "foo/" is included in these filenames. The filename is not allowed to + // contain "." or ".." components. + virtual io::ZeroCopyOutputStream* Open(const string& filename) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OutputDirectory); +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc new file mode 100644 index 00000000..68e88a8e --- /dev/null +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -0,0 +1,579 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace compiler { + +#if defined(_WIN32) +#define mkdir(name, mode) mkdir(name) +#ifndef W_OK +#define W_OK 02 // not defined by MSVC for whatever reason +#endif +#ifndef F_OK +#define F_OK 00 // not defined by MSVC for whatever reason +#endif +#endif + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +namespace { +#if defined(_WIN32) && !defined(__CYGWIN__) +static const char* kPathSeparator = ";"; +#else +static const char* kPathSeparator = ":"; +#endif +} // namespace + +// A MultiFileErrorCollector that prints errors to stderr. +class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector { + public: + ErrorPrinter() {} + ~ErrorPrinter() {} + + // implements MultiFileErrorCollector ------------------------------ + void AddError(const string& filename, int line, int column, + const string& message) { + // Users typically expect 1-based line/column numbers, so we add 1 + // to each here. + cerr << filename; + if (line != -1) { + cerr << ":" << (line + 1) << ":" << (column + 1); + } + cerr << ": " << message << endl; + } +}; + +// ------------------------------------------------------------------- + +// An OutputDirectory implementation that writes to disk. +class CommandLineInterface::DiskOutputDirectory : public OutputDirectory { + public: + DiskOutputDirectory(const string& root); + ~DiskOutputDirectory(); + + bool VerifyExistence(); + + inline bool had_error() { return had_error_; } + inline void set_had_error(bool value) { had_error_ = value; } + + // implements OutputDirectory -------------------------------------- + io::ZeroCopyOutputStream* Open(const string& filename); + + private: + string root_; + bool had_error_; +}; + +// A FileOutputStream that checks for errors in the destructor and reports +// them. We extend FileOutputStream via wrapping rather than inheritance +// for two reasons: +// 1) Implementation inheritance is evil. +// 2) We need to close the file descriptor *after* the FileOutputStream's +// destructor is run to make sure it flushes the file contents. +class CommandLineInterface::ErrorReportingFileOutput + : public io::ZeroCopyOutputStream { + public: + ErrorReportingFileOutput(int file_descriptor, + const string& filename, + DiskOutputDirectory* directory); + ~ErrorReportingFileOutput(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size) { return file_stream_->Next(data, size); } + void BackUp(int count) { file_stream_->BackUp(count); } + int64 ByteCount() const { return file_stream_->ByteCount(); } + + private: + scoped_ptr file_stream_; + int file_descriptor_; + string filename_; + DiskOutputDirectory* directory_; +}; + +// ------------------------------------------------------------------- + +CommandLineInterface::DiskOutputDirectory::DiskOutputDirectory( + const string& root) + : root_(root), had_error_(false) { + // Add a '/' to the end if it doesn't already have one. But don't add a + // '/' to an empty string since this probably means the current directory. + if (!root_.empty() && root[root_.size() - 1] != '/') { + root_ += '/'; + } +} + +CommandLineInterface::DiskOutputDirectory::~DiskOutputDirectory() { +} + +bool CommandLineInterface::DiskOutputDirectory::VerifyExistence() { + if (!root_.empty()) { + // Make sure the directory exists. If it isn't a directory, this will fail + // because we added a '/' to the end of the name in the constructor. + if (access(root_.c_str(), W_OK) == -1) { + cerr << root_ << ": " << strerror(errno) << endl; + return false; + } + } + + return true; +} + +io::ZeroCopyOutputStream* CommandLineInterface::DiskOutputDirectory::Open( + const string& filename) { + // Recursively create parent directories to the output file. + vector parts; + SplitStringUsing(filename, "/", &parts); + string path_so_far = root_; + for (int i = 0; i < parts.size() - 1; i++) { + path_so_far += parts[i]; + if (mkdir(path_so_far.c_str(), 0777) != 0) { + if (errno != EEXIST) { + cerr << filename << ": while trying to create directory " + << path_so_far << ": " << strerror(errno) << endl; + had_error_ = true; + // Return a dummy stream. + return new io::ArrayOutputStream(NULL, 0); + } + } + path_so_far += '/'; + } + + // Create the output file. + int file_descriptor; + do { + file_descriptor = + open((root_ + filename).c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0777); + } while (file_descriptor < 0 && errno == EINTR); + + if (file_descriptor < 0) { + // Failed to open. + cerr << filename << ": " << strerror(errno) << endl; + had_error_ = true; + // Return a dummy stream. + return new io::ArrayOutputStream(NULL, 0); + } + + return new ErrorReportingFileOutput(file_descriptor, filename, this); +} + +CommandLineInterface::ErrorReportingFileOutput::ErrorReportingFileOutput( + int file_descriptor, + const string& filename, + DiskOutputDirectory* directory) + : file_stream_(new io::FileOutputStream(file_descriptor)), + file_descriptor_(file_descriptor), + filename_(filename), + directory_(directory) {} + +CommandLineInterface::ErrorReportingFileOutput::~ErrorReportingFileOutput() { + // Check if we had any errors while writing. + if (file_stream_->GetErrno() != 0) { + cerr << filename_ << ": " << strerror(file_stream_->GetErrno()) << endl; + directory_->set_had_error(true); + } + + // Close the file stream. + if (!file_stream_->Close()) { + cerr << filename_ << ": " << strerror(file_stream_->GetErrno()) << endl; + directory_->set_had_error(true); + } +} + +// =================================================================== + +CommandLineInterface::CommandLineInterface() + : disallow_services_(false), + inputs_are_proto_path_relative_(false) {} +CommandLineInterface::~CommandLineInterface() {} + +void CommandLineInterface::RegisterGenerator(const string& flag_name, + CodeGenerator* generator, + const string& help_text) { + GeneratorInfo info; + info.generator = generator; + info.help_text = help_text; + generators_[flag_name] = info; +} + +int CommandLineInterface::Run(int argc, const char* const argv[]) { + Clear(); + if (!ParseArguments(argc, argv)) return -1; + + // Set up the source tree. + DiskSourceTree source_tree; + for (int i = 0; i < proto_path_.size(); i++) { + source_tree.MapPath(proto_path_[i].first, proto_path_[i].second); + } + + // Map input files to virtual paths if necessary. + if (!inputs_are_proto_path_relative_) { + if (!MakeInputsBeProtoPathRelative(&source_tree)) { + return -1; + } + } + + // Allocate the Importer. + ErrorPrinter error_collector; + DescriptorPool pool; + Importer importer(&source_tree, &error_collector); + + // Parse each file and generate output. + for (int i = 0; i < input_files_.size(); i++) { + // Import the file. + const FileDescriptor* parsed_file = importer.Import(input_files_[i]); + if (parsed_file == NULL) return -1; + + // Enforce --disallow_services. + if (disallow_services_ && parsed_file->service_count() > 0) { + cerr << parsed_file->name() << ": This file contains services, but " + "--disallow_services was used." << endl; + return -1; + } + + // Generate output files. + for (int i = 0; i < output_directives_.size(); i++) { + if (!GenerateOutput(parsed_file, output_directives_[i])) { + return -1; + } + } + } + + return 0; +} + +void CommandLineInterface::Clear() { + proto_path_.clear(); + input_files_.clear(); + output_directives_.clear(); +} + +bool CommandLineInterface::MakeInputsBeProtoPathRelative( + DiskSourceTree* source_tree) { + for (int i = 0; i < input_files_.size(); i++) { + string virtual_file, shadowing_disk_file; + switch (source_tree->DiskFileToVirtualFile( + input_files_[i], &virtual_file, &shadowing_disk_file)) { + case DiskSourceTree::SUCCESS: + input_files_[i] = virtual_file; + break; + case DiskSourceTree::SHADOWED: + cerr << input_files_[i] << ": Input is shadowed in the --proto_path " + "by \"" << shadowing_disk_file << "\". Either use the latter " + "file as your input or reorder the --proto_path so that the " + "former file's location comes first." << endl; + return false; + case DiskSourceTree::CANNOT_OPEN: + cerr << input_files_[i] << ": " << strerror(errno) << endl; + return false; + case DiskSourceTree::NO_MAPPING: + // First check if the file exists at all. + if (access(input_files_[i].c_str(), F_OK) < 0) { + // File does not even exist. + cerr << input_files_[i] << ": " << strerror(ENOENT) << endl; + } else { + cerr << input_files_[i] << ": File does not reside within any path " + "specified using --proto_path (or -I). You must specify a " + "--proto_path which encompasses this file." << endl; + } + return false; + } + } + + return true; +} + +bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { + executable_name_ = argv[0]; + + // Iterate through all arguments and parse them. + for (int i = 1; i < argc; i++) { + string name, value; + + if (ParseArgument(argv[i], &name, &value)) { + // Retured true => Use the next argument as the flag value. + if (i + 1 == argc || argv[i+1][0] == '-') { + cerr << "Missing value for flag: " << name << endl; + return false; + } else { + ++i; + value = argv[i]; + } + } + + if (!InterpretArgument(name, value)) return false; + } + + // If no --proto_path was given, use the current working directory. + if (proto_path_.empty()) { + proto_path_.push_back(make_pair("", ".")); + } + + // Check some errror cases. + if (input_files_.empty()) { + cerr << "Missing input file." << endl; + return false; + } + if (output_directives_.empty()) { + cerr << "Missing output directives." << endl; + return false; + } + + return true; +} + +bool CommandLineInterface::ParseArgument(const char* arg, + string* name, string* value) { + bool parsed_value = false; + + if (arg[0] != '-') { + // Not a flag. + name->clear(); + parsed_value = true; + *value = arg; + } else if (arg[1] == '-') { + // Two dashes: Multi-character name, with '=' separating name and + // value. + const char* equals_pos = strchr(arg, '='); + if (equals_pos != NULL) { + *name = string(arg, equals_pos - arg); + *value = equals_pos + 1; + parsed_value = true; + } else { + *name = arg; + } + } else { + // One dash: One-character name, all subsequent characters are the + // value. + if (arg[1] == '\0') { + // arg is just "-". We treat this as an input file, except that at + // present this will just lead to a "file not found" error. + name->clear(); + *value = arg; + parsed_value = true; + } else { + *name = string(arg, 2); + *value = arg + 2; + parsed_value = !value->empty(); + } + } + + // Need to return true iff the next arg should be used as the value for this + // one, false otherwise. + + if (parsed_value) { + // We already parsed a value for this flag. + return false; + } + + if (*name == "-h" || *name == "--help" || + *name == "--disallow_services" || + *name == "--version") { + // HACK: These are the only flags that don't take a value. + // They probably should not be hard-coded like this but for now it's + // not worth doing better. + return false; + } + + // Next argument is the flag value. + return true; +} + +bool CommandLineInterface::InterpretArgument(const string& name, + const string& value) { + if (name.empty()) { + // Not a flag. Just a filename. + if (value.empty()) { + cerr << "You seem to have passed an empty string as one of the " + "arguments to " << executable_name_ << ". This is actually " + "sort of hard to do. Congrats. Unfortunately it is not valid " + "input so the program is going to die now." << endl; + return false; + } + + input_files_.push_back(value); + + } else if (name == "-I" || name == "--proto_path") { + // Java's -classpath (and some other languages) delimits path components + // with colons. Let's accept that syntax too just to make things more + // intuitive. + vector parts; + SplitStringUsing(value, kPathSeparator, &parts); + + for (int i = 0; i < parts.size(); i++) { + string virtual_path; + string disk_path; + + int equals_pos = parts[i].find_first_of('='); + if (equals_pos == string::npos) { + virtual_path = ""; + disk_path = parts[i]; + } else { + virtual_path = parts[i].substr(0, equals_pos); + disk_path = parts[i].substr(equals_pos + 1); + } + + if (disk_path.empty()) { + cerr << "--proto_path passed empty directory name. (Use \".\" for " + "current directory.)" << endl; + return false; + } + + // Make sure disk path exists, warn otherwise. + if (access(disk_path.c_str(), F_OK) < 0) { + cerr << disk_path << ": warning: directory does not exist." << endl; + } + + proto_path_.push_back(make_pair(virtual_path, disk_path)); + } + + } else if (name == "-h" || name == "--help") { + PrintHelpText(); + return false; // Exit without running compiler. + + } else if (name == "--version") { + if (!version_info_.empty()) { + cout << version_info_ << endl; + } + cout << "libprotoc " + << protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION) + << endl; + return false; // Exit without running compiler. + + } else if (name == "--disallow_services") { + disallow_services_ = true; + + } else { + // Some other flag. Look it up in the generators list. + GeneratorMap::const_iterator iter = generators_.find(name); + if (iter == generators_.end()) { + cerr << "Unknown flag: " << name << endl; + return false; + } + + // It's an output flag. Add it to the output directives. + OutputDirective directive; + directive.name = name; + directive.generator = iter->second.generator; + + // Split value at ':' to separate the generator parameter from the + // filename. + vector parts; + SplitStringUsing(value, ":", &parts); + + if (parts.size() == 1) { + directive.output_location = parts[0]; + } else if (parts.size() == 2) { + directive.parameter = parts[0]; + directive.output_location = parts[1]; + } else { + cerr << "Invalid value for flag " << name << "." << endl; + return false; + } + + output_directives_.push_back(directive); + } + + return true; +} + +void CommandLineInterface::PrintHelpText() { + // Sorry for indentation here; line wrapping would be uglier. + cerr << +"Usage: " << executable_name_ << " [OPTION] PROTO_FILE\n" +"Parse PROTO_FILE and generate output based on the options given:\n" +" -IPATH, --proto_path=PATH Specify the directory in which to search for\n" +" imports. May be specified multiple times;\n" +" directories will be searched in order. If not\n" +" given, the current working directory is used.\n" +" --version Show version info and exit.\n" +" -h, --help Show this text and exit." << endl; + + for (GeneratorMap::iterator iter = generators_.begin(); + iter != generators_.end(); ++iter) { + // FIXME(kenton): If the text is long enough it will wrap, which is ugly, + // but fixing this nicely (e.g. splitting on spaces) is probably more + // trouble than it's worth. + cerr << " " << iter->first << "=OUT_DIR " + << string(19 - iter->first.size(), ' ') // Spaces for alignment. + << iter->second.help_text << endl; + } +} + +bool CommandLineInterface::GenerateOutput( + const FileDescriptor* parsed_file, + const OutputDirective& output_directive) { + // Create the output directory. + DiskOutputDirectory output_directory(output_directive.output_location); + if (!output_directory.VerifyExistence()) { + return false; + } + + // Opened successfully. Write it. + + // Call the generator. + string error; + if (!output_directive.generator->Generate( + parsed_file, output_directive.parameter, &output_directory, &error)) { + // Generator returned an error. + cerr << output_directive.name << ": " << error << endl; + return false; + } + + // Check for write errors. + if (output_directory.had_error()) { + return false; + } + + return true; +} + + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h new file mode 100644 index 00000000..d3cae75e --- /dev/null +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -0,0 +1,210 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Implements the Protocol Compiler front-end such that it may be reused by +// custom compilers written to support other languages. + +#ifndef GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__ +#define GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__ + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +class FileDescriptor; // descriptor.h + +namespace compiler { + +class CodeGenerator; // code_generator.h +class DiskSourceTree; // importer.h + +// This class implements the command-line interface to the protocol compiler. +// It is designed to make it very easy to create a custom protocol compiler +// supporting the languages of your choice. For example, if you wanted to +// create a custom protocol compiler binary which includes both the regular +// C++ support plus support for your own custom output "Foo", you would +// write a class "FooGenerator" which implements the CodeGenerator interface, +// then write a main() procedure like this: +// +// int main(int argc, char* argv[]) { +// google::protobuf::compiler::CommandLineInterface cli; +// +// // Support generation of C++ source and headers. +// google::protobuf::compiler::cpp::CppGenerator cpp_generator; +// cli.RegisterGenerator("--cpp_out", &cpp_generator, +// "Generate C++ source and header."); +// +// // Support generation of Foo code. +// FooGenerator foo_generator; +// cli.RegisterGenerator("--foo_out", &foo_generator, +// "Generate Foo file."); +// +// return cli.Run(argc, argv); +// } +// +// The compiler is invoked with syntax like: +// protoc --cpp_out=outdir --foo_out=outdir --proto_path=src foo.proto +// +// For a full description of the command-line syntax, invoke it with --help. +class LIBPROTOC_EXPORT CommandLineInterface { + public: + CommandLineInterface(); + ~CommandLineInterface(); + + // Register a code generator for a language. + // + // Parameters: + // * flag_name: The command-line flag used to specify an output file of + // this type. The name must start with a '-'. If the name is longer + // than one letter, it must start with two '-'s. + // * generator: The CodeGenerator which will be called to generate files + // of this type. + // * help_text: Text describing this flag in the --help output. + // + // Some generators accept extra parameters. You can specify this parameter + // on the command-line by placing it before the output directory, separated + // by a colon: + // protoc --foo_out=enable_bar:outdir + // The text before the colon is passed to CodeGenerator::Generate() as the + // "parameter". + void RegisterGenerator(const string& flag_name, + CodeGenerator* generator, + const string& help_text); + + // Run the Protocol Compiler with the given command-line parameters. + // Returns the error code which should be returned by main(). + // + // It may not be safe to call Run() in a multi-threaded environment because + // it calls strerror(). I'm not sure why you'd want to do this anyway. + int Run(int argc, const char* const argv[]); + + // Call SetInputsAreCwdRelative(true) if the input files given on the command + // line should be interpreted relative to the proto import path specified + // using --proto_path or -I flags. Otherwise, input file names will be + // interpreted relative to the current working directory (or as absolute + // paths if they start with '/'), though they must still reside inside + // a directory given by --proto_path or the compiler will fail. The latter + // mode is generally more intuitive and easier to use, especially e.g. when + // defining implicit rules in Makefiles. + void SetInputsAreProtoPathRelative(bool enable) { + inputs_are_proto_path_relative_ = enable; + } + + // Provides some text which will be printed when the --version flag is + // used. The version of libprotoc will also be printed on the next line + // after this text. + void SetVersionInfo(const string& text) { + version_info_ = text; + } + + + private: + // ----------------------------------------------------------------- + + class ErrorPrinter; + class DiskOutputDirectory; + class ErrorReportingFileOutput; + + // Clear state from previous Run(). + void Clear(); + + // Remaps each file in input_files_ so that it is relative to one of the + // directories in proto_path_. Returns false if an error occurred. This + // is only used if inputs_are_proto_path_relative_ is false. + bool MakeInputsBeProtoPathRelative( + DiskSourceTree* source_tree); + + // Parse all command-line arguments. + bool ParseArguments(int argc, const char* const argv[]); + + // Parses a command-line argument into a name/value pair. Returns + // true if the next argument in the argv should be used as the value, + // false otherwise. + // + // Exmaples: + // "-Isrc/protos" -> + // name = "-I", value = "src/protos" + // "--cpp_out=src/foo.pb2.cc" -> + // name = "--cpp_out", value = "src/foo.pb2.cc" + // "foo.proto" -> + // name = "", value = "foo.proto" + bool ParseArgument(const char* arg, string* name, string* value); + + // Interprets arguments parsed with ParseArgument. + bool InterpretArgument(const string& name, const string& value); + + // Print the --help text to stderr. + void PrintHelpText(); + + // Generate the given output file from the given input. + struct OutputDirective; // see below + bool GenerateOutput(const FileDescriptor* proto_file, + const OutputDirective& output_directive); + + // ----------------------------------------------------------------- + + // The name of the executable as invoked (i.e. argv[0]). + string executable_name_; + + // Version info set with SetVersionInfo(). + string version_info_; + + // Map from flag names to registered generators. + struct GeneratorInfo { + CodeGenerator* generator; + string help_text; + }; + typedef map GeneratorMap; + GeneratorMap generators_; + + // Stuff parsed from command line. + vector > proto_path_; // Search path for proto files. + vector input_files_; // Names of the input proto files. + + // output_directives_ lists all the files we are supposed to output and what + // generator to use for each. + struct OutputDirective { + string name; + CodeGenerator* generator; + string parameter; + string output_location; + }; + vector output_directives_; + + // Was the --disallow_services flag used? + bool disallow_services_; + + // See SetInputsAreProtoPathRelative(). + bool inputs_are_proto_path_relative_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CommandLineInterface); +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__ diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc new file mode 100644 index 00000000..1b1458de --- /dev/null +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -0,0 +1,964 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +namespace { + +class CommandLineInterfaceTest : public testing::Test { + protected: + virtual void SetUp(); + virtual void TearDown(); + + // Runs the CommandLineInterface with the given command line. The + // command is automatically split on spaces, and the string "$tmpdir" + // is replaced with TestTempDir(). + void Run(const string& command); + + // ----------------------------------------------------------------- + // Methods to set up the test (called before Run()). + + class MockCodeGenerator; + + // Registers a MockCodeGenerator with the given name. + MockCodeGenerator* RegisterGenerator(const string& generator_name, + const string& flag_name, + const string& filename, + const string& help_text); + MockCodeGenerator* RegisterErrorGenerator(const string& generator_name, + const string& error_text, + const string& flag_name, + const string& filename, + const string& help_text); + + // Create a temp file within temp_directory_ with the given name. + // The containing directory is also created if necessary. + void CreateTempFile(const string& name, const string& contents); + + void SetInputsAreProtoPathRelative(bool enable) { + cli_.SetInputsAreProtoPathRelative(enable); + } + + // ----------------------------------------------------------------- + // Methods to check the test results (called after Run()). + + // Checks that no text was written to stderr during Run(), and Run() + // returned 0. + void ExpectNoErrors(); + + // Checks that Run() returned non-zero and the stderr output is exactly + // the text given. expected_test may contain references to "$tmpdir", + // which will be replaced by the temporary directory path. + void ExpectErrorText(const string& expected_text); + + // Checks that Run() returned non-zero and the stderr contains the given + // substring. + void ExpectErrorSubstring(const string& expected_substring); + + // Returns true if ExpectErrorSubstring(expected_substring) would pass, but + // does not fail otherwise. + bool HasAlternateErrorSubstring(const string& expected_substring); + + // Checks that MockCodeGenerator::Generate() was called in the given + // context. That is, this tests if the generator with the given name + // was called with the given parameter and proto file and produced the + // given output file. This is checked by reading the output file and + // checking that it contains the content that MockCodeGenerator would + // generate given these inputs. message_name is the name of the first + // message that appeared in the proto file; this is just to make extra + // sure that the correct file was parsed. + void ExpectGenerated(const string& generator_name, + const string& parameter, + const string& proto_name, + const string& message_name, + const string& output_file); + + private: + // The object we are testing. + CommandLineInterface cli_; + + // We create a directory within TestTempDir() in order to add extra + // protection against accidentally deleting user files (since we recursively + // delete this directory during the test). This is the full path of that + // directory. + string temp_directory_; + + // The result of Run(). + int return_code_; + + // The captured stderr output. + string error_text_; + + // Pointers which need to be deleted later. + vector mock_generators_to_delete_; +}; + +// A mock CodeGenerator which outputs information about the context in which +// it was called, which can then be checked. Output is written to a filename +// constructed by concatenating the filename_prefix (given to the constructor) +// with the proto file name, separated by a '.'. +class CommandLineInterfaceTest::MockCodeGenerator : public CodeGenerator { + public: + // Create a MockCodeGenerator whose Generate() method returns true. + MockCodeGenerator(const string& name, const string& filename_prefix); + + // Create a MockCodeGenerator whose Generate() method returns false + // and sets the error string to the given string. + MockCodeGenerator(const string& name, const string& filename_prefix, + const string& error); + + ~MockCodeGenerator(); + + void set_expect_write_error(bool value) { + expect_write_error_ = value; + } + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + string name_; + string filename_prefix_; + bool return_error_; + string error_; + bool expect_write_error_; +}; + +// =================================================================== + +void CommandLineInterfaceTest::SetUp() { + // Most of these tests were written before this option was added, so we + // run with the option on (which used to be the only way) except in certain + // tests where we turn it off. + cli_.SetInputsAreProtoPathRelative(true); + + temp_directory_ = TestTempDir() + "/proto2_cli_test_temp"; + + // If the temp directory already exists, it must be left over from a + // previous run. Delete it. + if (File::Exists(temp_directory_)) { + File::DeleteRecursively(temp_directory_, NULL, NULL); + } + + // Create the temp directory. + GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE)); +} + +void CommandLineInterfaceTest::TearDown() { + // Delete the temp directory. + File::DeleteRecursively(temp_directory_, NULL, NULL); + + // Delete all the MockCodeGenerators. + for (int i = 0; i < mock_generators_to_delete_.size(); i++) { + delete mock_generators_to_delete_[i]; + } + mock_generators_to_delete_.clear(); +} + +void CommandLineInterfaceTest::Run(const string& command) { + vector args; + SplitStringUsing(command, " ", &args); + + scoped_array argv(new const char*[args.size()]); + + for (int i = 0; i < args.size(); i++) { + args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true); + argv[i] = args[i].c_str(); + } + + CaptureTestStderr(); + + return_code_ = cli_.Run(args.size(), argv.get()); + + error_text_ = GetCapturedTestStderr(); +} + +// ------------------------------------------------------------------- + +CommandLineInterfaceTest::MockCodeGenerator* +CommandLineInterfaceTest::RegisterGenerator( + const string& generator_name, + const string& flag_name, + const string& filename, + const string& help_text) { + MockCodeGenerator* generator = + new MockCodeGenerator(generator_name, filename); + mock_generators_to_delete_.push_back(generator); + + cli_.RegisterGenerator(flag_name, generator, help_text); + return generator; +} + +CommandLineInterfaceTest::MockCodeGenerator* +CommandLineInterfaceTest::RegisterErrorGenerator( + const string& generator_name, + const string& error_text, + const string& flag_name, + const string& filename_prefix, + const string& help_text) { + MockCodeGenerator* generator = + new MockCodeGenerator(generator_name, filename_prefix, error_text); + mock_generators_to_delete_.push_back(generator); + + cli_.RegisterGenerator(flag_name, generator, help_text); + return generator; +} + +void CommandLineInterfaceTest::CreateTempFile( + const string& name, + const string& contents) { + // Create parent directory, if necessary. + string::size_type slash_pos = name.find_last_of('/'); + if (slash_pos != string::npos) { + string dir = name.substr(0, slash_pos); + File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777); + } + + // Write file. + string full_name = temp_directory_ + "/" + name; + File::WriteStringToFileOrDie(contents, full_name); +} + +// ------------------------------------------------------------------- + +void CommandLineInterfaceTest::ExpectNoErrors() { + EXPECT_EQ(0, return_code_); + EXPECT_EQ("", error_text_); +} + +void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) { + EXPECT_NE(0, return_code_); + EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true), + error_text_); +} + +void CommandLineInterfaceTest::ExpectErrorSubstring( + const string& expected_substring) { + EXPECT_NE(0, return_code_); + EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_); +} + +bool CommandLineInterfaceTest::HasAlternateErrorSubstring( + const string& expected_substring) { + EXPECT_NE(0, return_code_); + return error_text_.find(expected_substring) != string::npos; +} + +void CommandLineInterfaceTest::ExpectGenerated( + const string& generator_name, + const string& parameter, + const string& proto_name, + const string& message_name, + const string& output_file_prefix) { + // Open and read the file. + string output_file = output_file_prefix + "." + proto_name; + string file_contents; + ASSERT_TRUE(File::ReadFileToString(temp_directory_ + "/" + output_file, + &file_contents)) + << "Failed to open file: " + output_file; + + // Check that the contents are as we expect. + string expected_contents = + generator_name + ": " + parameter + ", " + proto_name + ", " + + message_name + "\n"; + EXPECT_EQ(expected_contents, file_contents) + << "Output file did not have expected contents: " + output_file; +} + +// =================================================================== + +CommandLineInterfaceTest::MockCodeGenerator::MockCodeGenerator( + const string& name, const string& filename_prefix) + : name_(name), + filename_prefix_(filename_prefix), + return_error_(false), + expect_write_error_(false) { +} + +CommandLineInterfaceTest::MockCodeGenerator::MockCodeGenerator( + const string& name, const string& filename_prefix, const string& error) + : name_(name), + filename_prefix_(filename_prefix), + return_error_(true), + error_(error), + expect_write_error_(false) { +} + +CommandLineInterfaceTest::MockCodeGenerator::~MockCodeGenerator() {} + +bool CommandLineInterfaceTest::MockCodeGenerator::Generate( + const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + scoped_ptr output( + output_directory->Open(filename_prefix_ + "." + file->name())); + io::Printer printer(output.get(), '$'); + map vars; + vars["name"] = name_; + vars["parameter"] = parameter; + vars["proto_name"] = file->name(); + vars["message_name"] = file->message_type_count() > 0 ? + file->message_type(0)->full_name().c_str() : "(none)"; + + printer.Print(vars, "$name$: $parameter$, $proto_name$, $message_name$\n"); + + if (expect_write_error_) { + EXPECT_TRUE(printer.failed()); + } else { + EXPECT_FALSE(printer.failed()); + } + + *error = error_; + return !return_error_; +} + +// =================================================================== + +TEST_F(CommandLineInterfaceTest, BasicOutput) { + // Test that the common case works. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, MultipleInputs) { + // Test parsing multiple input files. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto bar.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); + ExpectGenerated("test_generator", "", "bar.proto", "Bar", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, CreateDirectory) { + // Test that when we output to a sub-directory, it is created. + + RegisterGenerator("test_generator", "--test_out", + "bar/baz/output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", + "foo.proto", "Foo", "bar/baz/output.test"); +} + +TEST_F(CommandLineInterfaceTest, GeneratorParameters) { + // Test that generator parameters are correctly parsed from the command line. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=TestParameter:$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "TestParameter", + "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, PathLookup) { + // Test that specifying multiple directories in the proto search path works. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("b/bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + CreateTempFile("a/foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "message Foo {\n" + " optional Bar a = 1;\n" + "}\n"); + CreateTempFile("b/foo.proto", "this should not be parsed\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) { + // Same as PathLookup, but we provide the proto_path in a single flag. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("b/bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + CreateTempFile("a/foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "message Foo {\n" + " optional Bar a = 1;\n" + "}\n"); + CreateTempFile("b/foo.proto", "this should not be parsed\n"); + +#undef PATH_SEPARATOR +#if defined(_WIN32) +#define PATH_SEPARATOR ";" +#else +#define PATH_SEPARATOR ":" +#endif + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/a"PATH_SEPARATOR"$tmpdir/b foo.proto"); + +#undef PATH_SEPARATOR + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, NonRootMapping) { + // Test setting up a search path mapping a directory to a non-root location. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=bar=$tmpdir bar/foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, MultipleGenerators) { + // Test that we can have multiple generators and use both in one invocation, + // each with a different output directory. + + RegisterGenerator("test_generator_1", "--test1_out", + "output1.test", "Test output 1."); + RegisterGenerator("test_generator_2", "--test2_out", + "output2.test", "Test output 2."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + // Create the "a" and "b" sub-directories. + CreateTempFile("a/dummy", ""); + CreateTempFile("b/dummy", ""); + + Run("protocol_compiler " + "--test1_out=$tmpdir/a " + "--test2_out=$tmpdir/b " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator_1", "", "foo.proto", "Foo", "a/output1.test"); + ExpectGenerated("test_generator_2", "", "foo.proto", "Foo", "b/output2.test"); +} + +TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) { + // Test that --disallow_services doesn't cause a problem when there are no + // services. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --disallow_services --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) { + // Test that --disallow_services produces an error when there are services. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n" + "service Bar {}\n"); + + Run("protocol_compiler --disallow_services --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring("foo.proto: This file contains services"); +} + +TEST_F(CommandLineInterfaceTest, AllowServicesHasService) { + // Test that services work fine as long as --disallow_services is not used. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n" + "service Bar {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) { + // Test that we can accept working-directory-relative input files. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir $tmpdir/foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +// ------------------------------------------------------------------- + +TEST_F(CommandLineInterfaceTest, ParseErrors) { + // Test that parse errors are reported. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "badsyntax\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorText( + "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n"); +} + +TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) { + // Test that parse errors are reported from multiple files. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + // We set up files such that foo.proto actually depends on bar.proto in + // two ways: Directly and through baz.proto. bar.proto's errors should + // only be reported once. + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "badsyntax\n"); + CreateTempFile("baz.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n"); + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "import \"baz.proto\";\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorText( + "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n" + "baz.proto: Import \"bar.proto\" was not found or had errors.\n" + "foo.proto: Import \"bar.proto\" was not found or had errors.\n" + "foo.proto: Import \"baz.proto\" was not found or had errors.\n"); +} + +TEST_F(CommandLineInterfaceTest, InputNotFoundError) { + // Test what happens if the input file is not found. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorText( + "foo.proto: File not found.\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) { + // Test what happens when a working-directory-relative input file is not + // found. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir $tmpdir/foo.proto"); + + ExpectErrorText( + "$tmpdir/foo.proto: No such file or directory\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) { + // Test what happens when a working-directory-relative input file is not + // mapped to a virtual path. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + // Create a directory called "bar" so that we can point --proto_path at it. + CreateTempFile("bar/dummy", ""); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/bar $tmpdir/foo.proto"); + + ExpectErrorText( + "$tmpdir/foo.proto: File does not reside within any path " + "specified using --proto_path (or -I). You must specify a " + "--proto_path which encompasses this file.\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) { + // Check what happens if the input file is not found *and* is not mapped + // in the proto_path. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + // Create a directory called "bar" so that we can point --proto_path at it. + CreateTempFile("bar/dummy", ""); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/bar $tmpdir/foo.proto"); + + ExpectErrorText( + "$tmpdir/foo.proto: No such file or directory\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) { + // Test what happens when a working-directory-relative input file is shadowed + // by another file in the virtual path. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo/foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar/foo.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar " + "$tmpdir/bar/foo.proto"); + + ExpectErrorText( + "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path " + "by \"$tmpdir/foo/foo.proto\". Either use the latter " + "file as your input or reorder the --proto_path so that the " + "former file's location comes first.\n"); +} + +TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) { + // Test what happens if the input file is not found. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/foo foo.proto"); + + ExpectErrorText( + "$tmpdir/foo: warning: directory does not exist.\n" + "foo.proto: File not found.\n"); +} + +TEST_F(CommandLineInterfaceTest, MissingInputError) { + // Test that we get an error if no inputs are given. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir"); + + ExpectErrorText("Missing input file.\n"); +} + +TEST_F(CommandLineInterfaceTest, MissingOutputError) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --proto_path=$tmpdir foo.proto"); + + ExpectErrorText("Missing output directives.\n"); +} + +TEST_F(CommandLineInterfaceTest, OutputWriteError) { + MockCodeGenerator* generator = + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + generator->set_expect_write_error(true); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + // Create a directory blocking our output location. + CreateTempFile("output.test.foo.proto/foo", ""); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + +#if defined(_WIN32) && !defined(__CYGWIN__) + // Windows with MSVCRT.dll produces EPERM instead of EISDIR. + if (HasAlternateErrorSubstring("output.test.foo.proto: Permission denied")) { + return; + } +#endif + + ExpectErrorSubstring("output.test.foo.proto: Is a directory"); +} + +TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir/nosuchdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring("nosuchdir/: " + "No such file or directory"); +} + +TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir/foo.proto " + "--proto_path=$tmpdir foo.proto"); + +#if defined(_WIN32) && !defined(__CYGWIN__) + // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR. + if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) { + return; + } +#endif + + ExpectErrorSubstring("foo.proto/: Not a directory"); +} + +TEST_F(CommandLineInterfaceTest, GeneratorError) { + RegisterErrorGenerator("error_generator", "Test error message.", + "--error_out", "output.test", "Test error output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --error_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring("--error_out: Test error message."); +} + +TEST_F(CommandLineInterfaceTest, HelpText) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + RegisterErrorGenerator("error_generator", "Test error message.", + "--error_out", "output.test", "Test error output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("test_exec_name --help"); + + ExpectErrorSubstring("Usage: test_exec_name "); + ExpectErrorSubstring("--test_out=OUT_DIR"); + ExpectErrorSubstring("Test output."); + ExpectErrorSubstring("--error_out=OUT_DIR"); + ExpectErrorSubstring("Test error output."); +} + +// ------------------------------------------------------------------- +// Flag parsing tests + +TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) { + // Test that a single-character flag works. + + RegisterGenerator("test_generator", "-o", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler -o$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) { + // Test that separating the flag value with a space works. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out $tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) { + // Test that separating the flag value with a space works for + // single-character flags. + + RegisterGenerator("test_generator", "-o", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler -o $tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, MissingValueError) { + // Test that we get an error if a flag is missing its value. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto"); + + ExpectErrorText("Missing value for flag: --test_out\n"); +} + +TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) { + // Test that we get an error if the last argument is a flag requiring a + // value. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out"); + + ExpectErrorText("Missing value for flag: --test_out\n"); +} + +} // anonymous namespace + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc new file mode 100644 index 00000000..daa66c8c --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc @@ -0,0 +1,135 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This test insures that google/protobuf/descriptor.pb.{h,cc} match exactly +// what would be generated by the protocol compiler. These files are not +// generated automatically at build time because they are compiled into the +// protocol compiler itself. So, if they were auto-generated, you'd have a +// chicken-and-egg problem. +// +// If this test fails, run the script +// "generate_descriptor_proto.sh" and add +// descriptor.pb.{h,cc} to your changelist. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +class MockErrorCollector : public MultiFileErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, int line, int column, + const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", + filename, line, column, message); + } +}; + +class MockOutputDirectory : public OutputDirectory { + public: + MockOutputDirectory() {} + ~MockOutputDirectory() { + STLDeleteValues(&files_); + } + + void ExpectFileMatches(const string& virtual_filename, + const string& physical_filename) { + string* expected_contents = FindPtrOrNull(files_, virtual_filename); + ASSERT_TRUE(expected_contents != NULL) + << "Generator failed to generate file: " << virtual_filename; + + string actual_contents; + File::ReadFileToStringOrDie( + TestSourceDir() + "/" + physical_filename, + &actual_contents); + EXPECT_TRUE(actual_contents == *expected_contents) + << physical_filename << " needs to be regenerated. Please run " + "generate_descriptor_proto.sh and add this file " + "to your CL."; + } + + // implements OutputDirectory -------------------------------------- + + virtual io::ZeroCopyOutputStream* Open(const string& filename) { + string** map_slot = &files_[filename]; + if (*map_slot != NULL) delete *map_slot; + *map_slot = new string; + + return new io::StringOutputStream(*map_slot); + } + + private: + map files_; +}; + +TEST(BootstrapTest, GeneratedDescriptorMatches) { + MockErrorCollector error_collector; + DiskSourceTree source_tree; + source_tree.MapPath("", TestSourceDir()); + Importer importer(&source_tree, &error_collector); + const FileDescriptor* proto_file = + importer.Import("google/protobuf/descriptor.proto"); + EXPECT_EQ("", error_collector.text_); + ASSERT_TRUE(proto_file != NULL); + + CppGenerator generator; + MockOutputDirectory output_directory; + string error; + string parameter; + parameter = "dllexport_decl=LIBPROTOBUF_EXPORT"; + ASSERT_TRUE(generator.Generate(proto_file, parameter, + &output_directory, &error)); + + output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.h", + "google/protobuf/descriptor.pb.h"); + output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.cc", + "google/protobuf/descriptor.pb.cc"); +} + +} // namespace + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc new file mode 100644 index 00000000..f78d60d8 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -0,0 +1,196 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor), + classname_(ClassName(descriptor, false)), + dllexport_decl_(dllexport_decl) { +} + +EnumGenerator::~EnumGenerator() {} + +void EnumGenerator::GenerateDefinition(io::Printer* printer) { + map vars; + vars["classname"] = classname_; + vars["short_name"] = descriptor_->name(); + + printer->Print(vars, "enum $classname$ {\n"); + printer->Indent(); + + const EnumValueDescriptor* min_value = descriptor_->value(0); + const EnumValueDescriptor* max_value = descriptor_->value(0); + + for (int i = 0; i < descriptor_->value_count(); i++) { + vars["name"] = descriptor_->value(i)->name(); + vars["number"] = SimpleItoa(descriptor_->value(i)->number()); + vars["prefix"] = (descriptor_->containing_type() == NULL) ? + "" : classname_ + "_"; + + printer->Print(vars, "$prefix$$name$ = $number$,\n"); + + if (descriptor_->value(i)->number() < min_value->number()) { + min_value = descriptor_->value(i); + } + if (descriptor_->value(i)->number() > max_value->number()) { + max_value = descriptor_->value(i); + } + } + + printer->Outdent(); + printer->Print("};\n"); + + vars["min_name"] = min_value->name(); + vars["max_name"] = max_value->name(); + + if (dllexport_decl_.empty()) { + vars["dllexport"] = ""; + } else { + vars["dllexport"] = dllexport_decl_ + " "; + } + + printer->Print(vars, + "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n" + "$dllexport$bool $classname$_IsValid(int value);\n" + "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n" + "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n" + "\n"); +} + +void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { + map vars; + vars["nested_name"] = descriptor_->name(); + vars["classname"] = classname_; + printer->Print(vars, "typedef $classname$ $nested_name$;\n"); + + for (int j = 0; j < descriptor_->value_count(); j++) { + vars["tag"] = descriptor_->value(j)->name(); + printer->Print(vars, + "static const $nested_name$ $tag$ = $classname$_$tag$;\n"); + } + + printer->Print(vars, + "static inline const ::google::protobuf::EnumDescriptor*\n" + "$nested_name$_descriptor() {\n" + " return $classname$_descriptor();\n" + "}\n" + "static inline bool $nested_name$_IsValid(int value) {\n" + " return $classname$_IsValid(value);\n" + "}\n" + "static const $nested_name$ $nested_name$_MIN =\n" + " $classname$_$nested_name$_MIN;\n" + "static const $nested_name$ $nested_name$_MAX =\n" + " $classname$_$nested_name$_MAX;\n"); +} + +void EnumGenerator::GenerateDescriptorInitializer( + io::Printer* printer, int index) { + map vars; + vars["classname"] = classname_; + vars["index"] = SimpleItoa(index); + + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "$classname$_descriptor_ = file->enum_type($index$);\n"); + } else { + vars["parent"] = ClassName(descriptor_->containing_type(), false); + printer->Print(vars, + "$classname$_descriptor_ = $parent$_descriptor_->enum_type($index$);\n"); + } +} + +void EnumGenerator::GenerateMethods(io::Printer* printer) { + map vars; + vars["classname"] = classname_; + vars["builddescriptorsname"] = + GlobalBuildDescriptorsName(descriptor_->file()->name()); + + printer->Print(vars, + "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n" + " if ($classname$_descriptor_ == NULL) $builddescriptorsname$();\n" + " return $classname$_descriptor_;\n" + "}\n" + "bool $classname$_IsValid(int value) {\n" + " switch(value) {\n"); + + // Multiple values may have the same number. Make sure we only cover + // each number once by first constructing a set containing all valid + // numbers, then printing a case statement for each element. + + set numbers; + for (int j = 0; j < descriptor_->value_count(); j++) { + const EnumValueDescriptor* value = descriptor_->value(j); + numbers.insert(value->number()); + } + + for (set::iterator iter = numbers.begin(); + iter != numbers.end(); ++iter) { + printer->Print( + " case $number$:\n", + "number", SimpleItoa(*iter)); + } + + printer->Print(vars, + " return true;\n" + " default:\n" + " return false;\n" + " }\n" + "}\n" + "\n"); + + if (descriptor_->containing_type() != NULL) { + // We need to "define" the static constants which were declared in the + // header, to give the linker a place to put them. Or at least the C++ + // standard says we have to. MSVC actually insists tha we do _not_ define + // them again in the .cc file. + printer->Print("#ifndef _MSC_VER\n"); + + vars["parent"] = ClassName(descriptor_->containing_type(), false); + vars["nested_name"] = descriptor_->name(); + for (int i = 0; i < descriptor_->value_count(); i++) { + vars["value"] = descriptor_->value(i)->name(); + printer->Print(vars, + "const $classname$ $parent$::$value$;\n"); + } + printer->Print(vars, + "const $classname$ $parent$::$nested_name$_MIN;\n" + "const $classname$ $parent$::$nested_name$_MAX;\n"); + + printer->Print("#endif // _MSC_VER\n"); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h new file mode 100644 index 00000000..b30997c9 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -0,0 +1,81 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit EnumGenerator(const EnumDescriptor* descriptor, + const string& dllexport_decl); + ~EnumGenerator(); + + // Header stuff. + + // Generate header code defining the enum. This code should be placed + // within the enum's package namespace, but NOT within any class, even for + // nested enums. + void GenerateDefinition(io::Printer* printer); + + // For enums nested within a message, generate code to import all the enum's + // symbols (e.g. the enum type name, all its values, etc.) into the class's + // namespace. This should be placed inside the class definition in the + // header. + void GenerateSymbolImports(io::Printer* printer); + + // Source file stuff. + + // Generate code that initializes the global variable storing the enum's + // descriptor. + void GenerateDescriptorInitializer(io::Printer* printer, int index); + + // Generate non-inline methods related to the enum, such as IsValidValue(). + // Goes in the .cc file. + void GenerateMethods(io::Printer* printer); + + private: + const EnumDescriptor* descriptor_; + string classname_; + string dllexport_decl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc new file mode 100644 index 00000000..e02d7d8a --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -0,0 +1,226 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetEnumVariables(const FieldDescriptor* descriptor, + map* variables) { + const EnumValueDescriptor* default_value = descriptor->default_value_enum(); + + (*variables)["name"] = FieldName(descriptor); + (*variables)["type"] = ClassName(descriptor->enum_type(), true); + (*variables)["default"] = SimpleItoa(default_value->number()); + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); +} + +} // namespace + +// =================================================================== + +EnumFieldGenerator:: +EnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +EnumFieldGenerator::~EnumFieldGenerator() {} + +void EnumFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "int $name$_;\n"); +} + +void EnumFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $name$() const;\n" + "inline void set_$name$($type$ value);\n"); +} + +void EnumFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $classname$::$name$() const {\n" + " return static_cast< $type$ >($name$_);\n" + "}\n" + "inline void $classname$::set_$name$($type$ value) {\n" + " GOOGLE_DCHECK($type$_IsValid(value));\n" + " _set_bit($index$);\n" + " $name$_ = value;\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void EnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "set_$name$(from.$name$());\n"); +} + +void EnumFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, ",\n$name$_($default$)"); +} + +void EnumFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "int value;\n" + "DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value));\n" + "if ($type$_IsValid(value)) {\n" + " set_$name$(static_cast< $type$ >(value));\n" + "} else {\n" + " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::WriteEnum(" + "$number$, this->$name$(), output));\n"); +} + +void EnumFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::EnumSize(this->$name$());\n"); +} + +// =================================================================== + +RepeatedEnumFieldGenerator:: +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} + +void RepeatedEnumFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "::google::protobuf::RepeatedField $name$_;\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField& $name$() const;\n" + "inline ::google::protobuf::RepeatedField* mutable_$name$();\n" + "inline $type$ $name$(int index) const;\n" + "inline void set_$name$(int index, $type$ value);\n" + "inline void add_$name$($type$ value);\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedField*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline $type$ $classname$::$name$(int index) const {\n" + " return static_cast< $type$ >($name$_.Get(index));\n" + "}\n" + "inline void $classname$::set_$name$(int index, $type$ value) {\n" + " GOOGLE_DCHECK($type$_IsValid(value));\n" + " $name$_.Set(index, value);\n" + "}\n" + "inline void $classname$::add_$name$($type$ value) {\n" + " GOOGLE_DCHECK($type$_IsValid(value));\n" + " $name$_.Add(value);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedEnumFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "int value;\n" + "DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value));\n" + "if ($type$_IsValid(value)) {\n" + " add_$name$(static_cast< $type$ >(value));\n" + "} else {\n" + " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::WriteEnum(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size += ::google::protobuf::internal::WireFormat::EnumSize(\n" + " this->$name$(i));\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.h b/src/google/protobuf/compiler/cpp/cpp_enum_field.h new file mode 100644 index 00000000..a297e961 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumFieldGenerator : public FieldGenerator { + public: + explicit EnumFieldGenerator(const FieldDescriptor* descriptor); + ~EnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); +}; + +class RepeatedEnumFieldGenerator : public FieldGenerator { + public: + explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedEnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.cc b/src/google/protobuf/compiler/cpp/cpp_extension.cc new file mode 100644 index 00000000..87da63d7 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_extension.cc @@ -0,0 +1,104 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor), + dllexport_decl_(dllexport_decl) { + // Construct type_traits_. + if (descriptor_->is_repeated()) { + type_traits_ = "Repeated"; + } + + switch (descriptor_->cpp_type()) { + case FieldDescriptor::CPPTYPE_ENUM: + type_traits_.append("EnumTypeTraits< "); + type_traits_.append(ClassName(descriptor_->enum_type(), true)); + type_traits_.append(" >"); + break; + case FieldDescriptor::CPPTYPE_STRING: + type_traits_.append("StringTypeTraits"); + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + type_traits_.append("MessageTypeTraits< "); + type_traits_.append(ClassName(descriptor_->message_type(), true)); + type_traits_.append(" >"); + break; + default: + type_traits_.append("PrimitiveTypeTraits< "); + type_traits_.append(PrimitiveTypeName(descriptor_->cpp_type())); + type_traits_.append(" >"); + break; + } +} + +ExtensionGenerator::~ExtensionGenerator() {} + +void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) { + map vars; + vars["extendee" ] = ClassName(descriptor_->containing_type(), true); + vars["type_traits"] = type_traits_; + vars["name" ] = descriptor_->name(); + + // If this is a class member, it needs to be declared "static". Otherwise, + // it needs to be "extern". + vars["qualifier"] = + (descriptor_->extension_scope() == NULL) ? "extern" : "static"; + + if (!dllexport_decl_.empty()) { + vars["qualifier"] = dllexport_decl_ + " " + vars["qualifier"]; + } + + printer->Print(vars, + "$qualifier$ ::google::protobuf::internal::ExtensionIdentifier< $extendee$,\n" + " ::google::protobuf::internal::$type_traits$ > $name$;\n"); +} + +void ExtensionGenerator::GenerateDefinition(io::Printer* printer) { + map vars; + vars["extendee" ] = ClassName(descriptor_->containing_type(), true); + vars["number" ] = SimpleItoa(descriptor_->number()); + vars["type_traits"] = type_traits_; + vars["name" ] = descriptor_->name(); + + // If this is a class member, it needs to be declared in its class scope. + vars["scope"] = (descriptor_->extension_scope() == NULL) ? "" : + ClassName(descriptor_->extension_scope(), false) + "::"; + + printer->Print(vars, + "::google::protobuf::internal::ExtensionIdentifier< $extendee$,\n" + " ::google::protobuf::internal::$type_traits$ > $scope$$name$($number$);\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.h b/src/google/protobuf/compiler/cpp/cpp_extension.h new file mode 100644 index 00000000..149dbca9 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_extension.h @@ -0,0 +1,68 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_EXTENSION_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_EXTENSION_H__ + +#include +#include + +namespace google { +namespace protobuf { + class FieldDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +// Generates code for an extension, which may be within the scope of some +// message or may be at file scope. This is much simpler than FieldGenerator +// since extensions are just simple identifiers with interesting types. +class ExtensionGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit ExtensionGenerator(const FieldDescriptor* descriptor, + const string& dllexport_decl); + ~ExtensionGenerator(); + + // Header stuff. + void GenerateDeclaration(io::Printer* printer); + + // Source file stuff. + void GenerateDefinition(io::Printer* printer); + + private: + const FieldDescriptor* descriptor_; + string type_traits_; + string dllexport_decl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc new file mode 100644 index 00000000..2b1041be --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -0,0 +1,83 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +FieldGenerator::~FieldGenerator() {} + +FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_( + new scoped_ptr[descriptor->field_count()]) { + // Construct all the FieldGenerators. + for (int i = 0; i < descriptor->field_count(); i++) { + field_generators_[i].reset(MakeGenerator(descriptor->field(i))); + } +} + +FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) { + if (field->is_repeated()) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + return new RepeatedMessageFieldGenerator(field); + case FieldDescriptor::CPPTYPE_STRING: + return new RepeatedStringFieldGenerator(field); + case FieldDescriptor::CPPTYPE_ENUM: + return new RepeatedEnumFieldGenerator(field); + default: + return new RepeatedPrimitiveFieldGenerator(field); + } + } else { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + return new MessageFieldGenerator(field); + case FieldDescriptor::CPPTYPE_STRING: + return new StringFieldGenerator(field); + case FieldDescriptor::CPPTYPE_ENUM: + return new EnumFieldGenerator(field); + default: + return new PrimitiveFieldGenerator(field); + } + } +} + +FieldGeneratorMap::~FieldGeneratorMap() {} + +const FieldGenerator& FieldGeneratorMap::get( + const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h new file mode 100644 index 00000000..d37eb962 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -0,0 +1,127 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class FieldGenerator { + public: + FieldGenerator() {} + virtual ~FieldGenerator(); + + // Generate lines of code declaring members fields of the message class + // needed to represent this field. These are placed inside the message + // class. + virtual void GeneratePrivateMembers(io::Printer* printer) const = 0; + + // Generate prototypes for all of the accessor functions related to this + // field. These are placed inside the class definition. + virtual void GenerateAccessorDeclarations(io::Printer* printer) const = 0; + + // Generate inline definitions of accessor functions for this field. + // These are placed inside the header after all class definitions. + virtual void GenerateInlineAccessorDefinitions( + io::Printer* printer) const = 0; + + // Generate definitions of accessors that aren't inlined. These are + // placed somewhere in the .cc file. + // Most field types don't need this, so the default implementation is empty. + virtual void GenerateNonInlineAccessorDefinitions( + io::Printer* printer) const {} + + // Generate lines of code (statements, not declarations) which clear the + // field. This is used to define the clear_$name$() method as well as + // the Clear() method for the whole message. + virtual void GenerateClearingCode(io::Printer* printer) const = 0; + + // Generate lines of code (statements, not declarations) which merges the + // contents of the field from the current message to the target message, + // which is stored in the generated code variable "from". + // This is used to fill in the MergeFrom method for the whole message. + // Details of this usage can be found in message.cc under the + // GenerateMergeFrom method. + virtual void GenerateMergingCode(io::Printer* printer) const = 0; + + // Generate any initializers needed for the private members declared by + // GeneratePrivateMembers(). These go into the message class's + // constructor's initializer list. For each initializer, this method + // must print the comma and newline separating it from the *previous* + // initializer, not the *next* initailizer. That is, print a ",\n" first, + // e.g.: + // printer->Print(",\n$name$_($default$)"); + virtual void GenerateInitializer(io::Printer* printer) const = 0; + + // Generate any code that needs to go in the class's destructor. + // Most field types don't need this, so the default implementation is empty. + virtual void GenerateDestructorCode(io::Printer* printer) const {} + + // Generate lines to decode this field, which will be placed inside the + // message's MergeFromCodedStream() method. + virtual void GenerateMergeFromCodedStream(io::Printer* printer) const = 0; + + // Generate lines to serialize this field, which are placed within the + // message's SerializeWithCachedSizes() method. + virtual void GenerateSerializeWithCachedSizes(io::Printer* printer) const = 0; + + // Generate lines to compute the serialized size of this field, which + // are placed in the message's ByteSize() method. + virtual void GenerateByteSize(io::Printer* printer) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); +}; + +// Convenience class which constructs FieldGenerators for a Descriptor. +class FieldGeneratorMap { + public: + explicit FieldGeneratorMap(const Descriptor* descriptor); + ~FieldGeneratorMap(); + + const FieldGenerator& get(const FieldDescriptor* field) const; + + private: + const Descriptor* descriptor_; + scoped_array > field_generators_; + + static FieldGenerator* MakeGenerator(const FieldDescriptor* field); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc new file mode 100644 index 00000000..aea3a4b2 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -0,0 +1,404 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// =================================================================== + +FileGenerator::FileGenerator(const FileDescriptor* file, + const string& dllexport_decl) + : file_(file), + message_generators_( + new scoped_ptr[file->message_type_count()]), + enum_generators_( + new scoped_ptr[file->enum_type_count()]), + service_generators_( + new scoped_ptr[file->service_count()]), + extension_generators_( + new scoped_ptr[file->extension_count()]) { + + for (int i = 0; i < file->message_type_count(); i++) { + message_generators_[i].reset( + new MessageGenerator(file->message_type(i), dllexport_decl)); + } + + for (int i = 0; i < file->enum_type_count(); i++) { + enum_generators_[i].reset( + new EnumGenerator(file->enum_type(i), dllexport_decl)); + } + + for (int i = 0; i < file->service_count(); i++) { + service_generators_[i].reset( + new ServiceGenerator(file->service(i), dllexport_decl)); + } + + for (int i = 0; i < file->extension_count(); i++) { + extension_generators_[i].reset( + new ExtensionGenerator(file->extension(i), dllexport_decl)); + } + + SplitStringUsing(file_->package(), ".", &package_parts_); +} + +FileGenerator::~FileGenerator() {} + +void FileGenerator::GenerateHeader(io::Printer* printer) { + string filename_identifier = FilenameIdentifier(file_->name()); + + // Generate top of header. + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n" + "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" + "#define PROTOBUF_$filename_identifier$__INCLUDED\n" + "\n" + "#include \n" + "\n", + "filename_identifier", filename_identifier); + + printer->Print( + "#include \n" + "\n"); + + // Verify the protobuf library header version is compatible with the protoc + // version before going any further. + printer->Print( + "#if GOOGLE_PROTOBUF_VERSION < $min_header_version$\n" + "#error This file was generated by a newer version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please update\n" + "#error your headers.\n" + "#endif\n" + "#if $protoc_version$ < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION\n" + "#error This file was generated by an older version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please\n" + "#error regenerate this file with a newer version of protoc.\n" + "#endif\n" + "\n", + "min_header_version", + SimpleItoa(protobuf::internal::kMinHeaderVersionForProtoc), + "protoc_version", SimpleItoa(GOOGLE_PROTOBUF_VERSION)); + + // OK, it's now safe to #include other files. + printer->Print( + "#include \n" + "#include \n" + "#include \n"); + + if (file_->service_count() > 0) { + printer->Print( + "#include \n"); + } + + for (int i = 0; i < file_->dependency_count(); i++) { + printer->Print( + "#include \"$dependency$.pb.h\"\n", + "dependency", StripProto(file_->dependency(i)->name())); + } + + // Open namespace. + GenerateNamespaceOpeners(printer); + + printer->Print("\n"); + + // Generate forward declarations of classes. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateForwardDeclaration(printer); + } + + printer->Print("\n"); + + // Generate enum definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateEnumDefinitions(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDefinition(printer); + } + + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Generate class definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateClassDefinition(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Generate service definitions. + for (int i = 0; i < file_->service_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + service_generators_[i]->GenerateDeclarations(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Declare extension identifiers. + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateDeclaration(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Generate class inline methods. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateInlineMethods(printer); + } + + // Close up namespace. + GenerateNamespaceClosers(printer); + + printer->Print( + "#endif // PROTOBUF_$filename_identifier$__INCLUDED\n", + "filename_identifier", filename_identifier); +} + +void FileGenerator::GenerateSource(io::Printer* printer) { + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n" + "#include \"$basename$.pb.h\"\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n", + "basename", StripProto(file_->name())); + + // For each dependency, write a prototype for that dependency's + // BuildDescriptors() function. We don't expose these in the header because + // they are internal implementation details, and since this is generated code + // we don't have the usual risks involved with declaring external functions + // within a .cc file. + for (int i = 0; i < file_->dependency_count(); i++) { + const FileDescriptor* dependency = file_->dependency(i); + // Open the dependency's namespace. + vector dependency_package_parts; + SplitStringUsing(dependency->package(), ".", &dependency_package_parts); + for (int i = 0; i < dependency_package_parts.size(); i++) { + printer->Print("namespace $name$ { ", + "name", dependency_package_parts[i]); + } + // Declare its BuildDescriptors() function. + printer->Print( + "void $function$();", + "function", GlobalBuildDescriptorsName(dependency->name())); + // Close the namespace. + for (int i = 0; i < dependency_package_parts.size(); i++) { + printer->Print(" }"); + } + printer->Print("\n"); + } + + GenerateNamespaceOpeners(printer); + + printer->Print( + "\n" + "namespace {\n" + "\n"); + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateDescriptorDeclarations(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + printer->Print( + "const ::google::protobuf::EnumDescriptor* $name$_descriptor_ = NULL;\n", + "name", ClassName(file_->enum_type(i), false)); + } + for (int i = 0; i < file_->service_count(); i++) { + printer->Print( + "const ::google::protobuf::ServiceDescriptor* $name$_descriptor_ = NULL;\n", + "name", file_->service(i)->name()); + } + + printer->Print( + "\n" + "} // namespace\n" + "\n"); + + // Define our externally-visible BuildDescriptors() function. + GenerateBuildDescriptors(printer); + + // Generate enums. + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateMethods(printer); + } + + // Generate classes. + for (int i = 0; i < file_->message_type_count(); i++) { + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + message_generators_[i]->GenerateClassMethods(printer); + } + + // Generate services. + for (int i = 0; i < file_->service_count(); i++) { + if (i == 0) printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + service_generators_[i]->GenerateImplementation(printer); + } + + // Define extensions. + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateDefinition(printer); + } + + GenerateNamespaceClosers(printer); +} + +void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { + // BuildDescriptors() is a file-level procedure which initializes all of + // the Descriptor objects for this file. It runs the first time one of the + // descriptors is accessed. This will always be at static initialization + // time, because every message has a statically-initialized default instance, + // and the constructor for a message class accesses its descriptor. See the + // constructor and the descriptor() method of message classes. + printer->Print( + "\n" + "void $builddescriptorsname$() {\n" + " static bool already_here = false;\n" + " if (already_here) return;\n" + " already_here = true;\n" + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + " ::google::protobuf::DescriptorPool* pool =\n" + " ::google::protobuf::DescriptorPool::internal_generated_pool();\n" + "\n", + "builddescriptorsname", GlobalBuildDescriptorsName(file_->name())); + printer->Indent(); + + // Call the BuildDescriptors() methods for all of our dependencies, to make + // sure they get initialized first. + for (int i = 0; i < file_->dependency_count(); i++) { + const FileDescriptor* dependency = file_->dependency(i); + // Print the namespace prefix for the dependency. + vector dependency_package_parts; + SplitStringUsing(dependency->package(), ".", &dependency_package_parts); + printer->Print("::"); + for (int i = 0; i < dependency_package_parts.size(); i++) { + printer->Print("$name$::", + "name", dependency_package_parts[i]); + } + // Call its BuildDescriptors function. + printer->Print( + "$name$();\n", + "name", GlobalBuildDescriptorsName(dependency->name())); + } + + // Embed the descriptor. We simply serialize the entire FileDescriptorProto + // and embed it as a string literal, which is parsed and built into real + // descriptors at initialization time. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + + printer->Print( + "const ::google::protobuf::FileDescriptor* file = pool->InternalBuildGeneratedFile("); + + // Only write 40 bytes per line. + static const int kBytesPerLine = 40; + for (int i = 0; i < file_data.size(); i += kBytesPerLine) { + printer->Print("\n \"$data$\"", + "data", CEscape(file_data.substr(i, kBytesPerLine))); + } + printer->Print( + ", $size$);\n", + "size", SimpleItoa(file_data.size())); + + // Assign all global descriptors. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + for (int i = 0; i < file_->service_count(); i++) { + service_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n" + "// Force BuildDescriptors() to be called at static initialization time.\n" + "struct StaticDescriptorInitializer_$filename$ {\n" + " StaticDescriptorInitializer_$filename$() {\n" + " $builddescriptorsname$();\n" + " }\n" + "} static_descriptor_initializer_$filename$_;\n" + "\n", + "builddescriptorsname", GlobalBuildDescriptorsName(file_->name()), + "filename", FilenameIdentifier(file_->name())); +} + +void FileGenerator::GenerateNamespaceOpeners(io::Printer* printer) { + if (package_parts_.size() > 0) printer->Print("\n"); + + for (int i = 0; i < package_parts_.size(); i++) { + printer->Print("namespace $part$ {\n", + "part", package_parts_[i]); + } +} + +void FileGenerator::GenerateNamespaceClosers(io::Printer* printer) { + if (package_parts_.size() > 0) printer->Print("\n"); + + for (int i = package_parts_.size() - 1; i >= 0; i--) { + printer->Print("} // namespace $part$\n", + "part", package_parts_[i]); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h new file mode 100644 index 00000000..f2255ee1 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + class FileDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumGenerator; // enum.h +class MessageGenerator; // message.h +class ServiceGenerator; // service.h +class ExtensionGenerator; // extension.h + +class FileGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit FileGenerator(const FileDescriptor* file, + const string& dllexport_decl); + ~FileGenerator(); + + void GenerateHeader(io::Printer* printer); + void GenerateSource(io::Printer* printer); + + private: + // Generate the BuildDescriptors() procedure, which builds all descriptors + // for types defined in the file. + void GenerateBuildDescriptors(io::Printer* printer); + + void GenerateNamespaceOpeners(io::Printer* printer); + void GenerateNamespaceClosers(io::Printer* printer); + + const FileDescriptor* file_; + + scoped_array > message_generators_; + scoped_array > enum_generators_; + scoped_array > service_generators_; + scoped_array > extension_generators_; + + // E.g. if the package is foo.bar, package_parts_ is {"foo", "bar"}. + vector package_parts_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc new file mode 100644 index 00000000..200f6358 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -0,0 +1,135 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +// Parses a set of comma-delimited name/value pairs, e.g.: +// "foo=bar,baz,qux=corge" +// parses to the pairs: +// ("foo", "bar"), ("baz", ""), ("qux", "corge") +void ParseOptions(const string& text, vector >* output) { + vector parts; + SplitStringUsing(text, ",", &parts); + + for (int i = 0; i < parts.size(); i++) { + string::size_type equals_pos = parts[i].find_first_of('='); + pair value; + if (equals_pos == string::npos) { + value.first = parts[i]; + value.second = ""; + } else { + value.first = parts[i].substr(0, equals_pos); + value.second = parts[i].substr(equals_pos + 1); + } + output->push_back(value); + } +} + +} // namespace + +CppGenerator::CppGenerator() {} +CppGenerator::~CppGenerator() {} + +bool CppGenerator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + vector > options; + ParseOptions(parameter, &options); + + // ----------------------------------------------------------------- + // parse generator options + + // TODO(kenton): If we ever have more options, we may want to create a + // class that encapsulates them which we can pass down to all the + // generator classes. Currently we pass dllexport_decl down to all of + // them via the constructors, but we don't want to have to add another + // constructor parameter for every option. + + // If the dllexport_decl option is passed to the compiler, we need to write + // it in front of every symbol that should be exported if this .proto is + // compiled into a Windows DLL. E.g., if the user invokes the protocol + // compiler as: + // protoc --cpp_out=dllexport_decl=FOO_EXPORT:outdir foo.proto + // then we'll define classes like this: + // class FOO_EXPORT Foo { + // ... + // } + // FOO_EXPORT is a macro which should expand to __declspec(dllexport) or + // __declspec(dllimport) depending on what is being compiled. + string dllexport_decl; + + for (int i = 0; i < options.size(); i++) { + if (options[i].first == "dllexport_decl") { + dllexport_decl = options[i].second; + } else { + *error = "Unknown generator option: " + options[i].first; + return false; + } + } + + // ----------------------------------------------------------------- + + + string basename = StripProto(file->name()); + basename.append(".pb"); + + FileGenerator file_generator(file, dllexport_decl); + + // Generate header. + { + scoped_ptr output( + output_directory->Open(basename + ".h")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateHeader(&printer); + } + + // Generate cc file. + { + scoped_ptr output( + output_directory->Open(basename + ".cc")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateSource(&printer); + } + + return true; +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.h b/src/google/protobuf/compiler/cpp/cpp_generator.h new file mode 100644 index 00000000..26fb8e97 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_generator.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Generates C++ code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// CodeGenerator implementation which generates a C++ source file and +// header. If you create your own protocol compiler binary and you want +// it to support C++ output, you can do so by registering an instance of this +// CodeGenerator with the CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT CppGenerator : public CodeGenerator { + public: + CppGenerator(); + ~CppGenerator(); + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CppGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc new file mode 100644 index 00000000..21de816c --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -0,0 +1,197 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +string DotsToUnderscores(const string& name) { + return StringReplace(name, ".", "_", true); +} + +string DotsToColons(const string& name) { + return StringReplace(name, ".", "::", true); +} + +const char* const kKeywordList[] = { + "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", + "catch", "char", "class", "compl", "const", "const_cast", "continue", + "default", "delete", "do", "double", "dynamic_cast", "else", "enum", + "explicit", "extern", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq", + "operator", "or", "or_eq", "private", "protected", "public", "register", + "reinterpret_cast", "return", "short", "signed", "sizeof", "static", + "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", + "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while", "xor", "xor_eq" +}; + +hash_set MakeKeywordsMap() { + hash_set result; + for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) { + result.insert(kKeywordList[i]); + } + return result; +} + +hash_set kKeywords = MakeKeywordsMap(); + +} // namespace + +const char kThickSeparator[] = + "// ===================================================================\n"; +const char kThinSeparator[] = + "// -------------------------------------------------------------------\n"; + +string ClassName(const Descriptor* descriptor, bool qualified) { + // Find "outer", the descriptor of the top-level message in which + // "descriptor" is embedded. + const Descriptor* outer = descriptor; + while (outer->containing_type() != NULL) outer = outer->containing_type(); + + const string& outer_name = outer->full_name(); + string inner_name = descriptor->full_name().substr(outer_name.size()); + + if (qualified) { + return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name); + } else { + return outer->name() + DotsToUnderscores(inner_name); + } +} + +string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) { + if (enum_descriptor->containing_type() == NULL) { + if (qualified) { + return DotsToColons(enum_descriptor->full_name()); + } else { + return enum_descriptor->name(); + } + } else { + string result = ClassName(enum_descriptor->containing_type(), qualified); + result += '_'; + result += enum_descriptor->name(); + return result; + } +} + +string FieldName(const FieldDescriptor* field) { + string result = field->name(); + LowerString(&result); + if (kKeywords.count(result) > 0) { + result.append("_"); + } + return result; +} + +string StripProto(const string& filename) { + if (HasSuffixString(filename, ".protodevel")) { + return StripSuffixString(filename, ".protodevel"); + } else { + return StripSuffixString(filename, ".proto"); + } +} + +const char* PrimitiveTypeName(FieldDescriptor::CppType type) { + switch (type) { + case FieldDescriptor::CPPTYPE_INT32 : return "::google::protobuf::int32"; + case FieldDescriptor::CPPTYPE_INT64 : return "::google::protobuf::int64"; + case FieldDescriptor::CPPTYPE_UINT32 : return "::google::protobuf::uint32"; + case FieldDescriptor::CPPTYPE_UINT64 : return "::google::protobuf::uint64"; + case FieldDescriptor::CPPTYPE_DOUBLE : return "double"; + case FieldDescriptor::CPPTYPE_FLOAT : return "float"; + case FieldDescriptor::CPPTYPE_BOOL : return "bool"; + case FieldDescriptor::CPPTYPE_ENUM : return "int"; + case FieldDescriptor::CPPTYPE_STRING : return "::std::string"; + case FieldDescriptor::CPPTYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // CppTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +const char* DeclaredTypeMethodName(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return "Int32"; + case FieldDescriptor::TYPE_INT64 : return "Int64"; + case FieldDescriptor::TYPE_UINT32 : return "UInt32"; + case FieldDescriptor::TYPE_UINT64 : return "UInt64"; + case FieldDescriptor::TYPE_SINT32 : return "SInt32"; + case FieldDescriptor::TYPE_SINT64 : return "SInt64"; + case FieldDescriptor::TYPE_FIXED32 : return "Fixed32"; + case FieldDescriptor::TYPE_FIXED64 : return "Fixed64"; + case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; + case FieldDescriptor::TYPE_SFIXED64: return "SFixed64"; + case FieldDescriptor::TYPE_FLOAT : return "Float"; + case FieldDescriptor::TYPE_DOUBLE : return "Double"; + + case FieldDescriptor::TYPE_BOOL : return "Bool"; + case FieldDescriptor::TYPE_ENUM : return "Enum"; + + case FieldDescriptor::TYPE_STRING : return "String"; + case FieldDescriptor::TYPE_BYTES : return "Bytes"; + case FieldDescriptor::TYPE_GROUP : return "Group"; + case FieldDescriptor::TYPE_MESSAGE : return "Message"; + + // No default because we want the compiler to complain if any new + // types are added. + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + +// Convert a file name into a valid identifier. +string FilenameIdentifier(const string& filename) { + string result; + for (int i = 0; i < filename.size(); i++) { + if (ascii_isalnum(filename[i])) { + result.push_back(filename[i]); + } else { + // Not alphanumeric. To avoid any possibility of name conflicts we + // use the hex code for the character. + result.push_back('_'); + char buffer[kFastToBufferSize]; + result.append(FastHexToBuffer(static_cast(filename[i]), buffer)); + } + } + return result; +} + +// Return the name of the BuildDescriptors() function for a given file. +string GlobalBuildDescriptorsName(const string& filename) { + return "proto_BuildDescriptors_" + FilenameIdentifier(filename); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h new file mode 100644 index 00000000..7f57d694 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -0,0 +1,86 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// Commonly-used separator comments. Thick is a line of '=', thin is a line +// of '-'. +extern const char kThickSeparator[]; +extern const char kThinSeparator[]; + +// Returns the non-nested type name for the given type. If "qualified" is +// true, prefix the type with the full namespace. For example, if you had: +// package foo.bar; +// message Baz { message Qux {} } +// Then the qualified ClassName for Qux would be: +// ::foo::bar::Baz_Qux +// While the non-qualified version would be: +// Baz_Qux +string ClassName(const Descriptor* descriptor, bool qualified); +string ClassName(const EnumDescriptor* enum_descriptor, bool qualified); + +// Get the (unqualified) name that should be used for this field in C++ code. +// The name is coerced to lower-case to emulate proto1 behavior. People +// should be using lowercase-with-underscores style for proto field names +// anyway, so normally this just returns field->name(). +string FieldName(const FieldDescriptor* field); + +// Returns the scope where the field was defined (for extensions, this is +// different from the message type to which the field applies). +inline const Descriptor* FieldScope(const FieldDescriptor* field) { + return field->is_extension() ? + field->extension_scope() : field->containing_type(); +} + +// Strips ".proto" or ".protodevel" from the end of a filename. +string StripProto(const string& filename); + +// Get the C++ type name for a primitive type (e.g. "double", "::google::protobuf::int32", etc.). +// Note: non-built-in type names will be qualified, meaning they will start +// with a ::. If you are using the type as a template parameter, you will +// need to insure there is a space between the < and the ::, because the +// ridiculous C++ standard defines "<:" to be a synonym for "[". +const char* PrimitiveTypeName(FieldDescriptor::CppType type); + +// Get the declared type name in CamelCase format, as is used e.g. for the +// methods of WireFormat. For example, TYPE_INT32 becomes "Int32". +const char* DeclaredTypeMethodName(FieldDescriptor::Type type); + +// Convert a file name into a valid identifier. +string FilenameIdentifier(const string& filename); + +// Return the name of the BuildDescriptors() function for a given file. +string GlobalBuildDescriptorsName(const string& filename); + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc new file mode 100644 index 00000000..002b0ad2 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -0,0 +1,1445 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { + // Print the field's proto-syntax definition as a comment. We don't want to + // print group bodies so we cut off after the first line. + string def = field->DebugString(); + printer->Print("// $def$\n", + "def", def.substr(0, def.find_first_of('\n'))); +} + +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +const char* kWireTypeNames[] = { + "VARINT", + "FIXED64", + "LENGTH_DELIMITED", + "START_GROUP", + "END_GROUP", + "FIXED32", +}; + +// Sort the fields of the given Descriptor by number into a new[]'d array +// and return it. +const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { + const FieldDescriptor** fields = + new const FieldDescriptor*[descriptor->field_count()]; + for (int i = 0; i < descriptor->field_count(); i++) { + fields[i] = descriptor->field(i); + } + sort(fields, fields + descriptor->field_count(), + FieldOrderingByNumber()); + return fields; +} + +// Functor for sorting extension ranges by their "start" field number. +struct ExtensionRangeSorter { + bool operator()(const Descriptor::ExtensionRange* left, + const Descriptor::ExtensionRange* right) const { + return left->start < right->start; + } +}; + +// Returns true if the message type has any required fields. If it doesn't, +// we can optimize out calls to its IsInitialized() method. +// +// already_seen is used to avoid checking the same type multiple times +// (and also to protect against recursion). +static bool HasRequiredFields( + const Descriptor* type, + hash_set* already_seen) { + if (already_seen->count(type) > 0) { + // Since the first occurrence of a required field causes the whole + // function to return true, we can assume that if the type is already + // in the cache it didn't have any required fields. + return false; + } + already_seen->insert(type); + + // If the type has extensions, an extension with message type could contain + // required fields, so we have to be conservative and assume such an + // extension exists. + if (type->extension_range_count() > 0) return true; + + for (int i = 0; i < type->field_count(); i++) { + const FieldDescriptor* field = type->field(i); + if (field->is_required()) { + return true; + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (HasRequiredFields(field->message_type(), already_seen)) { + return true; + } + } + } + + return false; +} + +static bool HasRequiredFields(const Descriptor* type) { + hash_set already_seen; + return HasRequiredFields(type, &already_seen); +} + +} + +// =================================================================== + +MessageGenerator::MessageGenerator(const Descriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor), + classname_(ClassName(descriptor, false)), + dllexport_decl_(dllexport_decl), + field_generators_(descriptor), + nested_generators_(new scoped_ptr[ + descriptor->nested_type_count()]), + enum_generators_(new scoped_ptr[ + descriptor->enum_type_count()]), + extension_generators_(new scoped_ptr[ + descriptor->extension_count()]) { + + for (int i = 0; i < descriptor->nested_type_count(); i++) { + nested_generators_[i].reset( + new MessageGenerator(descriptor->nested_type(i), dllexport_decl)); + } + + for (int i = 0; i < descriptor->enum_type_count(); i++) { + enum_generators_[i].reset( + new EnumGenerator(descriptor->enum_type(i), dllexport_decl)); + } + + for (int i = 0; i < descriptor->extension_count(); i++) { + extension_generators_[i].reset( + new ExtensionGenerator(descriptor->extension(i), dllexport_decl)); + } +} + +MessageGenerator::~MessageGenerator() {} + +void MessageGenerator:: +GenerateForwardDeclaration(io::Printer* printer) { + printer->Print("class $classname$;\n", + "classname", classname_); + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateForwardDeclaration(printer); + } +} + +void MessageGenerator:: +GenerateEnumDefinitions(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateEnumDefinitions(printer); + } + + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDefinition(printer); + } +} + +void MessageGenerator:: +GenerateFieldAccessorDeclarations(io::Printer* printer) { + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + map vars; + vars["name"] = FieldName(field); + + if (field->is_repeated()) { + printer->Print(vars, "inline int $name$_size() const;\n"); + } else { + printer->Print(vars, "inline bool has_$name$() const;\n"); + } + + printer->Print(vars, "inline void clear_$name$();\n"); + + // Generate type-specific accessor declarations. + field_generators_.get(field).GenerateAccessorDeclarations(printer); + + printer->Print("\n"); + } + + if (descriptor_->extension_range_count() > 0) { + // Generate accessors for extensions. + + // Normally I'd generate prototypes here and generate the actual + // definitions of these methods in GenerateFieldAccessorDefinitions, but + // the prototypes for these silly methods are so absurdly complicated that + // it meant way too much repitition. + // + // We use "_proto_TypeTraits" as a type name below because "TypeTraits" + // causes problems if the class has a nested message or enum type with that + // name and "_TypeTraits" is technically reserved for the C++ library since + // it starts with an underscore followed by a capital letter. + printer->Print( + // Has, Size, Clear + "template \n" + "inline bool HasExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) const {\n" + " return _extensions_.Has(id.number());\n" + "}\n" + "\n" + "template \n" + "inline void ClearExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) {\n" + " _extensions_.ClearExtension(id.number());\n" + "}\n" + "\n" + "template \n" + "inline int ExtensionSize(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) const {\n" + " return _extensions_.ExtensionSize(id.number());\n" + "}\n" + "\n" + + // Singular accessors + "template \n" + "inline typename _proto_TypeTraits::ConstType GetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) const {\n" + " return _proto_TypeTraits::Get(id.number(), _extensions_);\n" + "}\n" + "\n" + "template \n" + "inline typename _proto_TypeTraits::MutableType MutableExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) {\n" + " return _proto_TypeTraits::Mutable(id.number(), &_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline void SetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " typename _proto_TypeTraits::ConstType value) {\n" + " _proto_TypeTraits::Set(id.number(), value, &_extensions_);\n" + "}\n" + "\n" + + // Repeated accessors + "template \n" + "inline typename _proto_TypeTraits::ConstType GetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " int index) const {\n" + " return _proto_TypeTraits::Get(id.number(), _extensions_, index);\n" + "}\n" + "\n" + "template \n" + "inline typename _proto_TypeTraits::MutableType MutableExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " int index) {\n" + " return _proto_TypeTraits::Mutable(id.number(),index,&_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline void SetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " int index, typename _proto_TypeTraits::ConstType value) {\n" + " _proto_TypeTraits::Set(id.number(), index, value, &_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline typename _proto_TypeTraits::MutableType AddExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) {\n" + " return _proto_TypeTraits::Add(id.number(), &_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline void AddExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " typename _proto_TypeTraits::ConstType value) {\n" + " _proto_TypeTraits::Add(id.number(), value, &_extensions_);\n" + "}\n", + "classname", classname_); + } +} + +void MessageGenerator:: +GenerateFieldAccessorDefinitions(io::Printer* printer) { + printer->Print("// $classname$\n\n", "classname", classname_); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + map vars; + vars["name"] = FieldName(field); + vars["index"] = SimpleItoa(field->index()); + vars["classname"] = classname_; + + // Generate has_$name$() or $name$_size(). + if (field->is_repeated()) { + printer->Print(vars, + "inline int $classname$::$name$_size() const {\n" + " return $name$_.size();\n" + "}\n"); + } else { + // Singular field. + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return _has_bit($index$);\n" + "}\n"); + } + + // Generate clear_$name$() + printer->Print(vars, + "inline void $classname$::clear_$name$() {\n"); + + printer->Indent(); + field_generators_.get(field).GenerateClearingCode(printer); + printer->Outdent(); + + if (!field->is_repeated()) { + printer->Print(vars, " _clear_bit($index$);\n"); + } + + printer->Print("}\n"); + + // Generate type-specific accessors. + field_generators_.get(field).GenerateInlineAccessorDefinitions(printer); + + printer->Print("\n"); + } +} + +void MessageGenerator:: +GenerateClassDefinition(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateClassDefinition(printer); + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + map vars; + vars["classname"] = classname_; + vars["field_count"] = SimpleItoa(descriptor_->field_count()); + if (dllexport_decl_.empty()) { + vars["dllexport"] = ""; + } else { + vars["dllexport"] = dllexport_decl_ + " "; + } + + printer->Print(vars, + "class $dllexport$$classname$ : public ::google::protobuf::Message {\n" + " public:\n"); + printer->Indent(); + + printer->Print(vars, + "$classname$();\n" + "virtual ~$classname$();\n" + "\n" + "$classname$(const $classname$& from);\n" + "\n" + "inline $classname$& operator=(const $classname$& from) {\n" + " CopyFrom(from);\n" + " return *this;\n" + "}\n" + "\n" + "inline static const $classname$& default_instance() {\n" + " return default_instance_;\n" + "}\n" + "\n" + "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" + " return _reflection_.unknown_fields();\n" + "}\n" + "\n" + "inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {\n" + " return _reflection_.mutable_unknown_fields();\n" + "}\n" + "\n" + "static const ::google::protobuf::Descriptor* descriptor();\n" + "\n" + "// implements Message ----------------------------------------------\n" + "\n" + "$classname$* New() const;\n"); + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print(vars, + "void CopyFrom(const ::google::protobuf::Message& from);\n" + "void MergeFrom(const ::google::protobuf::Message& from);\n" + "void CopyFrom(const $classname$& from);\n" + "void MergeFrom(const $classname$& from);\n" + "void Clear();\n" + "bool IsInitialized() const;\n" + "int ByteSize() const;\n" + "\n" + "bool MergePartialFromCodedStream(\n" + " ::google::protobuf::io::CodedInputStream* input);\n" + "bool SerializeWithCachedSizes(\n" + " ::google::protobuf::io::CodedOutputStream* output) const;\n"); + } + + printer->Print(vars, + "int GetCachedSize() const { return _cached_size_; }\n" + "private:\n" + "void SetCachedSize(int size) const { _cached_size_ = size; }\n" + "public:\n" + "\n" + "const ::google::protobuf::Descriptor* GetDescriptor() const;\n" + "const ::google::protobuf::Message::Reflection* GetReflection() const;\n" + "::google::protobuf::Message::Reflection* GetReflection();\n" + "\n" + "// nested types ----------------------------------------------------\n" + "\n"); + + // Import all nested message classes into this class's scope with typedefs. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + const Descriptor* nested_type = descriptor_->nested_type(i); + printer->Print("typedef $nested_full_name$ $nested_name$;\n", + "nested_name", nested_type->name(), + "nested_full_name", ClassName(nested_type, false)); + } + + if (descriptor_->nested_type_count() > 0) { + printer->Print("\n"); + } + + // Import all nested enums and their values into this class's scope with + // typedefs and constants. + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateSymbolImports(printer); + printer->Print("\n"); + } + + printer->Print( + "// accessors -------------------------------------------------------\n" + "\n"); + + // Generate accessor methods for all fields. + GenerateFieldAccessorDeclarations(printer); + + // Declare extension identifiers. + for (int i = 0; i < descriptor_->extension_count(); i++) { + extension_generators_[i]->GenerateDeclaration(printer); + } + + // Generate private members for fields. + printer->Outdent(); + printer->Print(" private:\n"); + printer->Indent(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "::google::protobuf::internal::ExtensionSet _extensions_;\n"); + } + + // TODO(kenton): Make _cached_size_ an atomic when C++ supports it. + printer->Print( + "::google::protobuf::internal::GeneratedMessageReflection _reflection_;\n" + "mutable int _cached_size_;\n" + "\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GeneratePrivateMembers(printer); + } + + // Generate offsets and _has_bits_ boilerplate. + printer->Print(vars, + "\n" + "static const $classname$ default_instance_;\n"); + + if (descriptor_->field_count() > 0) { + printer->Print(vars, + "static const int _offsets_[$field_count$];\n" + "\n" + "::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n"); + } else { + // Zero-size arrays aren't technically allowed, and MSVC in particular + // doesn't like them. We still need to declare these arrays to make + // other code compile. Since this is an uncommon case, we'll just declare + // them with size 1 and waste some space. Oh well. + printer->Print( + "static const int _offsets_[1];\n" + "\n" + "::google::protobuf::uint32 _has_bits_[1];\n"); + } + + printer->Print( + "\n" + "// WHY DOES & HAVE LOWER PRECEDENCE THAN != !?\n" + "inline bool _has_bit(int index) const {\n" + " return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;\n" + "}\n" + "inline void _set_bit(int index) {\n" + " _has_bits_[index / 32] |= (1u << (index % 32));\n" + "}\n" + "inline void _clear_bit(int index) {\n" + " _has_bits_[index / 32] &= ~(1u << (index % 32));\n" + "}\n"); + + printer->Outdent(); + printer->Print(vars, "};"); +} + +void MessageGenerator:: +GenerateInlineMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateInlineMethods(printer); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + GenerateFieldAccessorDefinitions(printer); +} + +void MessageGenerator:: +GenerateDescriptorDeclarations(io::Printer* printer) { + printer->Print("const ::google::protobuf::Descriptor* $name$_descriptor_ = NULL;\n", + "name", classname_); + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateDescriptorDeclarations(printer); + } + + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + printer->Print( + "const ::google::protobuf::EnumDescriptor* $name$_descriptor_ = NULL;\n", + "name", ClassName(descriptor_->enum_type(i), false)); + } +} + +void MessageGenerator:: +GenerateDescriptorInitializer(io::Printer* printer, int index) { + // TODO(kenton): Passing the index to this method is redundant; just use + // descriptor_->index() instead. + map vars; + vars["classname"] = classname_; + vars["index"] = SimpleItoa(index); + + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "$classname$_descriptor_ = file->message_type($index$);\n"); + } else { + vars["parent"] = ClassName(descriptor_->containing_type(), false); + printer->Print(vars, + "$classname$_descriptor_ = " + "$parent$_descriptor_->nested_type($index$);\n"); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + + // Register this message type with the message factory. + printer->Print(vars, + "::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(\n" + " $classname$_descriptor_, &$classname$::default_instance());\n"); +} + +void MessageGenerator:: +GenerateClassMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateMethods(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateClassMethods(printer); + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + printer->Print( + "const $classname$ $classname$::default_instance_;\n" + "\n", + "classname", classname_); + + // Generate non-inline field definitions. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateNonInlineAccessorDefinitions(printer); + printer->Print("\n"); + } + + // Define extension identifiers. + for (int i = 0; i < descriptor_->extension_count(); i++) { + extension_generators_[i]->GenerateDefinition(printer); + } + + GenerateOffsets(printer); + printer->Print("\n"); + + GenerateStructors(printer); + printer->Print("\n"); + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateClear(printer); + printer->Print("\n"); + + GenerateMergeFromCodedStream(printer); + printer->Print("\n"); + + GenerateSerializeWithCachedSizes(printer); + printer->Print("\n"); + + GenerateByteSize(printer); + printer->Print("\n"); + + GenerateMergeFrom(printer); + printer->Print("\n"); + + GenerateCopyFrom(printer); + printer->Print("\n"); + + GenerateIsInitialized(printer); + printer->Print("\n"); + } + + printer->Print( + "const ::google::protobuf::Descriptor* $classname$::GetDescriptor() const {\n" + " return descriptor();\n" + "}\n" + "\n" + "const ::google::protobuf::Message::Reflection*\n" + "$classname$::GetReflection() const {\n" + " return &_reflection_;\n" + "}\n" + "\n" + "::google::protobuf::Message::Reflection* $classname$::GetReflection() {\n" + " return &_reflection_;\n" + "}\n", + "classname", classname_); +} + +void MessageGenerator:: +GenerateOffsets(io::Printer* printer) { + printer->Print( + "const int $classname$::_offsets_[$field_count$] = {\n", + "classname", classname_, + "field_count", SimpleItoa(max(1, descriptor_->field_count()))); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + printer->Print( + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, $name$_),\n", + "classname", classname_, + "name", FieldName(field)); + } + + printer->Outdent(); + printer->Print("};\n"); +} + +void MessageGenerator:: +GenerateInitializerList(io::Printer* printer) { + printer->Indent(); + printer->Indent(); + + bool has_extensions = descriptor_->extension_range_count() > 0; + if (has_extensions) { + printer->Print( + "_extensions_(descriptor(),\n" + " ::google::protobuf::DescriptorPool::generated_pool(),\n" + " ::google::protobuf::MessageFactory::generated_factory()),\n"); + } + + printer->Print( + "_reflection_(descriptor(),\n" + " this, &default_instance_,\n" + " _offsets_, _has_bits_, $extensions$),\n" + "_cached_size_(0)", + "extensions", has_extensions ? "&_extensions_" : "NULL"); + + // Write the initializers for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateInitializer(printer); + } + + printer->Outdent(); + printer->Outdent(); +} + +void MessageGenerator:: +GenerateStructors(io::Printer* printer) { + // Generate the default constructor. + printer->Print( + "$classname$::$classname$()\n" + " : ", + "classname", classname_); + GenerateInitializerList(printer); + printer->Print(" {\n" + " ::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + " if (this == &default_instance_) {\n"); + + // The default instance needs all of its embedded message pointers + // cross-linked to other default instances. + // TODO(kenton): Maybe all message fields (even for non-default messages) + // should be initialized to point at default instances rather than NULL? + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print( + " $name$_ = const_cast< $type$*>(&$type$::default_instance());\n", + "name", FieldName(field), + "type", ClassName(field->message_type(), true)); + } + } + printer->Print( + " }\n" + "}\n" + "\n"); + + // Generate the copy constructor. + printer->Print( + "$classname$::$classname$(const $classname$& from)\n" + " : ", + "classname", classname_); + GenerateInitializerList(printer); + printer->Print(" {\n" + " ::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + " MergeFrom(from);\n" + "}\n" + "\n"); + + // Generate the destructor. + printer->Print( + "$classname$::~$classname$() {\n", + "classname", classname_); + + printer->Indent(); + + // Write the destructors for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateDestructorCode(printer); + } + + printer->Print( + "if (this != &default_instance_) {\n"); + + // We need to delete all embedded messages. + // TODO(kenton): If we make unset messages point at default instances + // instead of NULL, then it would make sense to move this code into + // MessageFieldGenerator::GenerateDestructorCode(). + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print(" delete $name$_;\n", + "name", FieldName(field)); + } + } + + printer->Outdent(); + + printer->Print( + " }\n" + "}\n" + "\n" + "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" + " if ($classname$_descriptor_ == NULL) $builddescriptorsname$();\n" + " return $classname$_descriptor_;\n" + "}\n" + "\n" + "$classname$* $classname$::New() const {\n" + " return new $classname$;\n" + "}\n", + "classname", classname_, + "builddescriptorsname", + GlobalBuildDescriptorsName(descriptor_->file()->name())); +} + +void MessageGenerator:: +GenerateClear(io::Printer* printer) { + printer->Print("void $classname$::Clear() {\n", + "classname", classname_); + printer->Indent(); + + int last_index = -1; + + if (descriptor_->extension_range_count() > 0) { + printer->Print("_extensions_.Clear();\n"); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated()) { + map vars; + vars["index"] = SimpleItoa(field->index()); + + // We can use the fact that _has_bits_ is a giant bitfield to our + // advantage: We can check up to 32 bits at a time for equality to + // zero, and skip the whole range if so. This can improve the speed + // of Clear() for messages which contain a very large number of + // optional fields of which only a few are used at a time. Here, + // we've chosen to check 8 bits at a time rather than 32. + if (i / 8 != last_index / 8 || last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print(vars, + "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n"); + printer->Indent(); + } + last_index = i; + + // It's faster to just overwrite primitive types, but we should + // only clear strings and messages if they were set. + // TODO(kenton): Let the CppFieldGenerator decide this somehow. + bool should_check_bit = + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || + field->cpp_type() == FieldDescriptor::CPPTYPE_STRING; + + if (should_check_bit) { + printer->Print(vars, "if (_has_bit($index$)) {\n"); + printer->Indent(); + } + + field_generators_.get(field).GenerateClearingCode(printer); + + if (should_check_bit) { + printer->Outdent(); + printer->Print("}\n"); + } + } + } + + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + + // Repeated fields don't use _has_bits_ so we clear them in a separate + // pass. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_repeated()) { + field_generators_.get(field).GenerateClearingCode(printer); + } + } + + printer->Print( + "::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + "mutable_unknown_fields()->Clear();\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateMergeFrom(io::Printer* printer) { + // Generate the generalized MergeFrom (aka that which takes in the Message + // base class as a parameter). + printer->Print( + "void $classname$::MergeFrom(const ::google::protobuf::Message& from) {\n" + " GOOGLE_CHECK_NE(&from, this);\n", + "classname", classname_); + printer->Indent(); + + if (descriptor_->field_count() > 0) { + // Cast the message to the proper type. If we find that the message is + // *not* of the proper type, we can still call Merge via the reflection + // system, as the GOOGLE_CHECK above ensured that we have the same descriptor + // for each message. + printer->Print( + "const $classname$* source =\n" + " ::google::protobuf::internal::dynamic_cast_if_available(\n" + " &from);\n" + "if (source == NULL) {\n" + " ::google::protobuf::internal::ReflectionOps::Merge(\n" + " descriptor(), *from.GetReflection(), &_reflection_);\n" + "} else {\n" + " MergeFrom(*source);\n" + "}\n", + "classname", classname_); + } + + printer->Outdent(); + printer->Print("}\n\n"); + + // Generate the class-specific MergeFrom, which avoids the GOOGLE_CHECK and cast. + printer->Print( + "void $classname$::MergeFrom(const $classname$& from) {\n" + " GOOGLE_CHECK_NE(&from, this);\n", + "classname", classname_); + printer->Indent(); + + // Merge Repeated fields. These fields do not require a + // check as we can simply iterate over them. + for (int i = 0; i < descriptor_->field_count(); ++i) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_repeated()) { + field_generators_.get(field).GenerateMergingCode(printer); + } + } + + // Merge Optional and Required fields (after a _has_bit check). + int last_index = -1; + + for (int i = 0; i < descriptor_->field_count(); ++i) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated()) { + map vars; + vars["index"] = SimpleItoa(field->index()); + + // See above in GenerateClear for an explanation of this. + if (i / 8 != last_index / 8 || last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print(vars, + "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n"); + printer->Indent(); + } + + last_index = i; + + printer->Print(vars, + "if (from._has_bit($index$)) {\n"); + printer->Indent(); + + field_generators_.get(field).GenerateMergingCode(printer); + + printer->Outdent(); + printer->Print("}\n"); + } + } + + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print("_extensions_.MergeFrom(from._extensions_);\n"); + } + + printer->Print( + "mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateCopyFrom(io::Printer* printer) { + // Generate the generalized CopyFrom (aka that which takes in the Message + // base class as a parameter). + printer->Print( + "void $classname$::CopyFrom(const ::google::protobuf::Message& from) {\n", + "classname", classname_); + printer->Indent(); + + printer->Print( + "if (&from == this) return;\n" + "Clear();\n" + "MergeFrom(from);\n"); + + printer->Outdent(); + printer->Print("}\n\n"); + + // Generate the class-specific CopyFrom. + printer->Print( + "void $classname$::CopyFrom(const $classname$& from) {\n", + "classname", classname_); + printer->Indent(); + + printer->Print( + "if (&from == this) return;\n" + "Clear();\n" + "MergeFrom(from);\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) { + if (descriptor_->options().message_set_wire_format()) { + // For message_set_wire_format, we don't generate a parser, for two + // reasons: + // - WireFormat already needs to special-case this, and we'd like to + // avoid having multiple implementations of MessageSet wire format + // lying around the code base. + // - All fields are extensions, and extension parsing falls back to + // reflection anyway, so it wouldn't be any faster. + printer->Print( + "bool $classname$::MergePartialFromCodedStream(\n" + " ::google::protobuf::io::CodedInputStream* input) {\n" + " return ::google::protobuf::internal::WireFormat::ParseAndMergePartial(\n" + " descriptor(), input, &_reflection_);\n" + "}\n", + "classname", classname_); + return; + } + + printer->Print( + "bool $classname$::MergePartialFromCodedStream(\n" + " ::google::protobuf::io::CodedInputStream* input) {\n" + "#define DO_(EXPRESSION) if (!(EXPRESSION)) return false\n" + " ::google::protobuf::uint32 tag;\n" + " while ((tag = input->ReadTag()) != 0) {\n", + "classname", classname_); + + printer->Indent(); + printer->Indent(); + + if (descriptor_->field_count() > 0) { + // We don't even want to print the switch() if we have no fields because + // MSVC dislikes switch() statements that contain only a default value. + + // Note: If we just switched on the tag rather than the field number, we + // could avoid the need for the if() to check the wire type at the beginning + // of each case. However, this is actually a bit slower in practice as it + // creates a jump table that is 8x larger and sparser, and meanwhile the + // if()s are highly predictable. + printer->Print( + "switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) {\n"); + + printer->Indent(); + + scoped_array ordered_fields( + SortFieldsByNumber(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = ordered_fields[i]; + + PrintFieldComment(printer, field); + + printer->Print( + "case $number$: {\n" + " if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) !=\n" + " ::google::protobuf::internal::WireFormat::WIRETYPE_$wiretype$) {\n" + " goto handle_uninterpreted;\n" + " }\n", + "number", SimpleItoa(field->number()), + "wiretype", kWireTypeNames[ + WireFormat::WireTypeForFieldType(field->type())]); + + if (i > 0 || field->is_repeated()) { + printer->Print( + " parse_$name$:\n", + "name", field->name()); + } + + printer->Indent(); + + field_generators_.get(field).GenerateMergeFromCodedStream(printer); + + // switch() is slow since it can't be predicted well. Insert some if()s + // here that attempt to predict the next tag. + if (field->is_repeated()) { + // Expect repeats of this field. + printer->Print( + "if (input->ExpectTag($tag$)) goto parse_$name$;\n", + "tag", SimpleItoa(WireFormat::MakeTag(field)), + "name", field->name()); + } + + if (i + 1 < descriptor_->field_count()) { + // Expect the next field in order. + const FieldDescriptor* next_field = ordered_fields[i + 1]; + printer->Print( + "if (input->ExpectTag($next_tag$)) goto parse_$next_name$;\n", + "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), + "next_name", next_field->name()); + } else { + // Expect EOF. + // TODO(kenton): Expect group end-tag? + printer->Print( + "if (input->ExpectAtEnd()) return true;\n"); + } + + printer->Print( + "break;\n"); + + printer->Outdent(); + printer->Print("}\n\n"); + } + + printer->Print( + "default: {\n" + "handle_uninterpreted:\n"); + printer->Indent(); + } + + // Is this an end-group tag? If so, this must be the end of the message. + printer->Print( + "if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) ==\n" + " ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) {\n" + " return true;\n" + "}\n"); + + // Handle extension ranges. + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if ("); + for (int i = 0; i < descriptor_->extension_range_count(); i++) { + const Descriptor::ExtensionRange* range = + descriptor_->extension_range(i); + if (i > 0) printer->Print(" &&\n "); + + uint32 start_tag = WireFormat::MakeTag( + range->start, static_cast(0)); + uint32 end_tag = WireFormat::MakeTag( + range->end, static_cast(0)); + + if (range->end > FieldDescriptor::kMaxNumber) { + printer->Print( + "($start$u <= tag)", + "start", SimpleItoa(start_tag)); + } else { + printer->Print( + "($start$u <= tag && tag < $end$u)", + "start", SimpleItoa(start_tag), + "end", SimpleItoa(end_tag)); + } + } + printer->Print(") {\n" + " DO_(_extensions_.ParseField(tag, input, &_reflection_));\n" + " continue;\n" + "}\n"); + } + + // We really don't recognize this tag. Skip it. + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" + " input, tag, mutable_unknown_fields()));\n"); + + if (descriptor_->field_count() > 0) { + printer->Print("break;\n"); + printer->Outdent(); + printer->Print("}\n"); // default: + printer->Outdent(); + printer->Print("}\n"); // switch + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // while + " return true;\n" + "#undef DO_\n" + "}\n"); +} + +void MessageGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + PrintFieldComment(printer, field); + + if (field->is_repeated()) { + printer->Print( + "for (int i = 0; i < $name$_.size(); i++) {\n", + "name", FieldName(field)); + } else { + printer->Print( + "if (_has_bit($index$)) {\n", + "index", SimpleItoa(field->index())); + } + + printer->Indent(); + + field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); + + printer->Outdent(); + printer->Print("}\n\n"); +} + +void MessageGenerator::GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range) { + map vars; + vars["start"] = SimpleItoa(range->start); + vars["end"] = SimpleItoa(range->end); + printer->Print(vars, + "// Extension range [$start$, $end$)\n" + "DO_(_extensions_.SerializeWithCachedSizes(\n" + " $start$, $end$, &_reflection_, output));\n\n"); +} + +void MessageGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) { + printer->Print( + "bool $classname$::SerializeWithCachedSizes(\n" + " ::google::protobuf::io::CodedOutputStream* output) const {\n" + "#define DO_(EXPRESSION) if (!(EXPRESSION)) return false\n", + "classname", classname_); + printer->Indent(); + + scoped_array ordered_fields( + SortFieldsByNumber(descriptor_)); + + vector sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeSorter()); + + // Merge the fields and the extension ranges, both sorted by field number. + int i, j; + for (i = 0, j = 0; + i < descriptor_->field_count() || j < sorted_extensions.size(); + ) { + if (i == descriptor_->field_count()) { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } else if (j == sorted_extensions.size()) { + GenerateSerializeOneField(printer, ordered_fields[i++]); + } else if (ordered_fields[i]->number() < sorted_extensions[j]->start) { + GenerateSerializeOneField(printer, ordered_fields[i++]); + } else { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } + } + + printer->Print("if (!unknown_fields().empty()) {\n"); + printer->Indent(); + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n" + " unknown_fields(), output));\n"); + } else { + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" + " unknown_fields(), output));\n"); + } + printer->Outdent(); + printer->Print( + "}\n" + "return true;\n"); + + printer->Outdent(); + printer->Print( + "#undef DO_\n" + "}\n"); +} + +void MessageGenerator:: +GenerateByteSize(io::Printer* printer) { + printer->Print( + "int $classname$::ByteSize() const {\n", + "classname", classname_); + printer->Indent(); + printer->Print( + "int total_size = 0;\n" + "\n"); + + int last_index = -1; + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated()) { + // See above in GenerateClear for an explanation of this. + // TODO(kenton): Share code? Unclear how to do so without + // over-engineering. + if ((i / 8) != (last_index / 8) || + last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print( + "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", + "index", SimpleItoa(field->index())); + printer->Indent(); + } + last_index = i; + + PrintFieldComment(printer, field); + + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + + field_generators_.get(field).GenerateByteSize(printer); + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + } + } + + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + + // Repeated fields don't use _has_bits_ so we count them in a separate + // pass. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_repeated()) { + PrintFieldComment(printer, field); + field_generators_.get(field).GenerateByteSize(printer); + printer->Print("\n"); + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "total_size += _extensions_.ByteSize(&_reflection_);\n" + "\n"); + } + + printer->Print("if (!unknown_fields().empty()) {\n"); + printer->Indent(); + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "total_size +=\n" + " ::google::protobuf::internal::WireFormat::ComputeUnknownMessageSetItemsSize(\n" + " unknown_fields());\n"); + } else { + printer->Print( + "total_size +=\n" + " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" + " unknown_fields());\n"); + } + printer->Outdent(); + printer->Print("}\n"); + + // We update _cached_size_ even though this is a const method. In theory, + // this is not thread-compatible, because concurrent writes have undefined + // results. In practice, since any concurrent writes will be writing the + // exact same value, it works on all common processors. In a future version + // of C++, _cached_size_ should be made into an atomic. + printer->Print( + "_cached_size_ = total_size;\n" + "return total_size;\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateIsInitialized(io::Printer* printer) { + printer->Print( + "bool $classname$::IsInitialized() const {\n", + "classname", classname_); + printer->Indent(); + + // Check that all required fields in this message are set. We can do this + // most efficiently by checking 32 "has bits" at a time. + int has_bits_array_size = (descriptor_->field_count() + 31) / 32; + for (int i = 0; i < has_bits_array_size; i++) { + uint32 mask = 0; + for (int bit = 0; bit < 32; bit++) { + int index = i * 32 + bit; + if (index >= descriptor_->field_count()) break; + const FieldDescriptor* field = descriptor_->field(index); + + if (field->is_required()) { + mask |= 1 << bit; + } + } + + if (mask != 0) { + char buffer[kFastToBufferSize]; + printer->Print( + "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", + "i", SimpleItoa(i), + "mask", FastHex32ToBuffer(mask, buffer)); + } + } + + // Now check that all embedded messages are initialized. + printer->Print("\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + if (field->is_repeated()) { + printer->Print( + "for (int i = 0; i < $name$_size(); i++) {\n" + " if (!this->$name$(i).IsInitialized()) return false;\n" + "}\n", + "name", FieldName(field)); + } else { + printer->Print( + "if (has_$name$()) {\n" + " if (!this->$name$().IsInitialized()) return false;\n" + "}\n", + "name", FieldName(field)); + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "\n" + "if (!_extensions_.IsInitialized()) return false;"); + } + + printer->Outdent(); + printer->Print( + " return true;\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h new file mode 100644 index 00000000..904e0487 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -0,0 +1,125 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumGenerator; // enum.h +class ExtensionGenerator; // extension.h + +class MessageGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit MessageGenerator(const Descriptor* descriptor, + const string& dllexport_decl); + ~MessageGenerator(); + + // Header stuff. + + // Generate foward declarations for this class and all its nested types. + void GenerateForwardDeclaration(io::Printer* printer); + + // Generate definitions of all nested enums (must come before class + // definitions because those classes use the enums definitions). + void GenerateEnumDefinitions(io::Printer* printer); + + // Generate definitions for this class and all its nested types. + void GenerateClassDefinition(io::Printer* printer); + + // Generate definitions of inline methods (placed at the end of the header + // file). + void GenerateInlineMethods(io::Printer* printer); + + // Source file stuff. + + // Generate code which declares all the global descriptor pointers which + // will be initialized by the methods below. + void GenerateDescriptorDeclarations(io::Printer* printer); + + // Generate code that initializes the global variable storing the message's + // descriptor. + void GenerateDescriptorInitializer(io::Printer* printer, int index); + + // Generate all non-inline methods for this class. + void GenerateClassMethods(io::Printer* printer); + + private: + // Generate declarations and definitions of accessors for fields. + void GenerateFieldAccessorDeclarations(io::Printer* printer); + void GenerateFieldAccessorDefinitions(io::Printer* printer); + + // Generate the field offsets array. + void GenerateOffsets(io::Printer* printer); + + // Generate constructors and destructor. + void GenerateStructors(io::Printer* printer); + + // Generate the member initializer list for the constructors. The member + // initializer list is shared between the default constructor and the copy + // constructor. + void GenerateInitializerList(io::Printer* printer); + + // Generate standard Message methods. + void GenerateClear(io::Printer* printer); + void GenerateMergeFromCodedStream(io::Printer* printer); + void GenerateSerializeWithCachedSizes(io::Printer* printer); + void GenerateByteSize(io::Printer* printer); + void GenerateMergeFrom(io::Printer* printer); + void GenerateCopyFrom(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer); + + // Helpers for GenerateSerializeWithCachedSizes(). + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + const Descriptor* descriptor_; + string classname_; + string dllexport_decl_; + FieldGeneratorMap field_generators_; + scoped_array > nested_generators_; + scoped_array > enum_generators_; + scoped_array > extension_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc new file mode 100644 index 00000000..501ca569 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -0,0 +1,229 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetMessageVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = FieldName(descriptor); + (*variables)["type"] = ClassName(descriptor->message_type(), true); + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type()); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); +} + +} // namespace + +// =================================================================== + +MessageFieldGenerator:: +MessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +MessageFieldGenerator::~MessageFieldGenerator() {} + +void MessageFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "$type$* $name$_;\n"); +} + +void MessageFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const $type$& $name$() const;\n" + "inline $type$* mutable_$name$();\n"); +} + +void MessageFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const $type$& $classname$::$name$() const {\n" + " return $name$_ != NULL ? *$name$_ : *default_instance_.$name$_;\n" + "}\n" + "inline $type$* $classname$::mutable_$name$() {\n" + " _set_bit($index$);\n" + " if ($name$_ == NULL) $name$_ = new $type$;\n" + " return $name$_;\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($name$_ != NULL) $name$_->$type$::Clear();\n"); +} + +void MessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "mutable_$name$()->$type$::MergeFrom(from.$name$());\n"); +} + +void MessageFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, ",\n$name$_(NULL)"); +} + +void MessageFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual(\n" + " input, mutable_$name$()));\n"); + } else { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadGroupNoVirtual(" + "$number$, input, mutable_$name$()));\n"); + } +} + +void MessageFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" + "$number$, this->$name$(), output));\n"); +} + +void MessageFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::$declared_type$SizeNoVirtual(\n" + " this->$name$());\n"); +} + +// =================================================================== + +RepeatedMessageFieldGenerator:: +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} + +void RepeatedMessageFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::google::protobuf::RepeatedPtrField< $type$ > $name$_;\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< $type$ >& $name$() const;\n" + "inline ::google::protobuf::RepeatedPtrField< $type$ >* mutable_$name$();\n" + "inline const $type$& $name$(int index) const;\n" + "inline $type$* mutable_$name$(int index);\n" + "inline $type$* add_$name$();\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedPtrField< $type$ >*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline const $type$& $classname$::$name$(int index) const {\n" + " return $name$_.Get(index);\n" + "}\n" + "inline $type$* $classname$::mutable_$name$(int index) {\n" + " return $name$_.Mutable(index);\n" + "}\n" + "inline $type$* $classname$::add_$name$() {\n" + " return $name$_.Add();\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedMessageFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual(\n" + " input, add_$name$()));\n"); + } else { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadGroupNoVirtual(" + "$number$, input, add_$name$()));\n"); + } +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size +=\n" + " ::google::protobuf::internal::WireFormat::$declared_type$SizeNoVirtual(\n" + " this->$name$(i));\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h new file mode 100644 index 00000000..a2be6b65 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class MessageFieldGenerator : public FieldGenerator { + public: + explicit MessageFieldGenerator(const FieldDescriptor* descriptor); + ~MessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); +}; + +class RepeatedMessageFieldGenerator : public FieldGenerator { + public: + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedMessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc new file mode 100644 index 00000000..312ed031 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -0,0 +1,294 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// For encodings with fixed sizes, returns that size in bytes. Otherwise +// returns -1. +int FixedSize(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return -1; + case FieldDescriptor::TYPE_INT64 : return -1; + case FieldDescriptor::TYPE_UINT32 : return -1; + case FieldDescriptor::TYPE_UINT64 : return -1; + case FieldDescriptor::TYPE_SINT32 : return -1; + case FieldDescriptor::TYPE_SINT64 : return -1; + case FieldDescriptor::TYPE_FIXED32 : return WireFormat::kFixed32Size; + case FieldDescriptor::TYPE_FIXED64 : return WireFormat::kFixed64Size; + case FieldDescriptor::TYPE_SFIXED32: return WireFormat::kSFixed32Size; + case FieldDescriptor::TYPE_SFIXED64: return WireFormat::kSFixed64Size; + case FieldDescriptor::TYPE_FLOAT : return WireFormat::kFloatSize; + case FieldDescriptor::TYPE_DOUBLE : return WireFormat::kDoubleSize; + + case FieldDescriptor::TYPE_BOOL : return WireFormat::kBoolSize; + case FieldDescriptor::TYPE_ENUM : return -1; + + case FieldDescriptor::TYPE_STRING : return -1; + case FieldDescriptor::TYPE_BYTES : return -1; + case FieldDescriptor::TYPE_GROUP : return -1; + case FieldDescriptor::TYPE_MESSAGE : return -1; + + // No default because we want the compiler to complain if any new + // types are added. + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return -1; +} + +string DefaultValue(const FieldDescriptor* field) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(field->default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + return SimpleItoa(field->default_value_uint32()) + "u"; + case FieldDescriptor::CPPTYPE_INT64: + return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")"; + case FieldDescriptor::CPPTYPE_UINT64: + return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")"; + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field->default_value_double()); + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field->default_value_float()); + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Shouldn't get here."; + return ""; + } + // Can't actually get here; make compiler happy. (We could add a default + // case above but then we wouldn't get the nice compiler warning when a + // new type is added.) + return ""; +} + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = FieldName(descriptor); + (*variables)["type"] = PrimitiveTypeName(descriptor->cpp_type()); + (*variables)["default"] = DefaultValue(descriptor); + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type()); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); + + int fixed_size = FixedSize(descriptor->type()); + if (fixed_size != -1) { + (*variables)["fixed_size"] = SimpleItoa(fixed_size); + } +} + +} // namespace + +// =================================================================== + +PrimitiveFieldGenerator:: +PrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} + +void PrimitiveFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "$type$ $name$_;\n"); +} + +void PrimitiveFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $name$() const;\n" + "inline void set_$name$($type$ value);\n"); +} + +void PrimitiveFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline void $classname$::set_$name$($type$ value) {\n" + " _set_bit($index$);\n" + " $name$_ = value;\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void PrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "set_$name$(from.$name$());\n"); +} + +void PrimitiveFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, ",\n$name$_($default$)"); +} + +void PrimitiveFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(\n" + " input, &$name$_));\n" + "_set_bit($index$);\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(), output));\n"); +} + +void PrimitiveFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + int fixed_size = FixedSize(descriptor_->type()); + if (fixed_size == -1) { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" + " this->$name$());\n"); + } else { + printer->Print(variables_, + "total_size += $tag_size$ + $fixed_size$;\n"); + } +} + +// =================================================================== + +RepeatedPrimitiveFieldGenerator:: +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} + +void RepeatedPrimitiveFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::google::protobuf::RepeatedField< $type$ > $name$_;\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField< $type$ >& $name$() const;\n" + "inline ::google::protobuf::RepeatedField< $type$ >* mutable_$name$();\n" + "inline $type$ $name$(int index) const;\n" + "inline void set_$name$(int index, $type$ value);\n" + "inline void add_$name$($type$ value);\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField< $type$ >&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedField< $type$ >*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline $type$ $classname$::$name$(int index) const {\n" + " return $name$_.Get(index);\n" + "}\n" + "inline void $classname$::set_$name$(int index, $type$ value) {\n" + " $name$_.Set(index, value);\n" + "}\n" + "inline void $classname$::add_$name$($type$ value) {\n" + " $name$_.Add(value);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "$type$ value;\n" + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(input, &value));\n" + "add_$name$(value);\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + int fixed_size = FixedSize(descriptor_->type()); + if (fixed_size == -1) { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size += ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" + " this->$name$(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "total_size += ($tag_size$ + $fixed_size$) * $name$_size();\n"); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h new file mode 100644 index 00000000..832b2411 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class PrimitiveFieldGenerator : public FieldGenerator { + public: + explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~PrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); +}; + +class RepeatedPrimitiveFieldGenerator : public FieldGenerator { + public: + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedPrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_service.cc b/src/google/protobuf/compiler/cpp/cpp_service.cc new file mode 100644 index 00000000..124f3b43 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_service.cc @@ -0,0 +1,318 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor) { + vars_["classname"] = descriptor_->name(); + vars_["full_name"] = descriptor_->full_name(); + if (dllexport_decl.empty()) { + vars_["dllexport"] = ""; + } else { + vars_["dllexport"] = dllexport_decl + " "; + } +} + +ServiceGenerator::~ServiceGenerator() {} + +void ServiceGenerator::GenerateDeclarations(io::Printer* printer) { + // Forward-declare the stub type. + printer->Print(vars_, + "class $classname$_Stub;\n" + "\n"); + + GenerateInterface(printer); + GenerateStubDefinition(printer); +} + +void ServiceGenerator::GenerateInterface(io::Printer* printer) { + printer->Print(vars_, + "class $dllexport$$classname$ : public ::google::protobuf::Service {\n" + " protected:\n" + " // This class should be treated as an abstract interface.\n" + " inline $classname$() {};\n" + " public:\n" + " virtual ~$classname$();\n"); + printer->Indent(); + + printer->Print(vars_, + "\n" + "typedef $classname$_Stub Stub;\n" + "\n" + "static const ::google::protobuf::ServiceDescriptor* descriptor();\n" + "\n"); + + GenerateMethodSignatures(VIRTUAL, printer); + + printer->Print( + "\n" + "// implements Service ----------------------------------------------\n" + "\n" + "const ::google::protobuf::ServiceDescriptor* GetDescriptor();\n" + "void CallMethod(const ::google::protobuf::MethodDescriptor* method,\n" + " ::google::protobuf::RpcController* controller,\n" + " const ::google::protobuf::Message* request,\n" + " ::google::protobuf::Message* response,\n" + " ::google::protobuf::Closure* done);\n" + "const ::google::protobuf::Message& GetRequestPrototype(\n" + " const ::google::protobuf::MethodDescriptor* method) const;\n" + "const ::google::protobuf::Message& GetResponsePrototype(\n" + " const ::google::protobuf::MethodDescriptor* method) const;\n"); + + printer->Outdent(); + printer->Print(vars_, + "\n" + " private:\n" + " GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($classname$);\n" + "};\n" + "\n"); +} + +void ServiceGenerator::GenerateStubDefinition(io::Printer* printer) { + printer->Print(vars_, + "class $dllexport$$classname$_Stub : public $classname$ {\n" + " public:\n"); + + printer->Indent(); + + printer->Print(vars_, + "$classname$_Stub(::google::protobuf::RpcChannel* channel);\n" + "$classname$_Stub(::google::protobuf::RpcChannel* channel,\n" + " ::google::protobuf::Service::ChannelOwnership ownership);\n" + "~$classname$_Stub();\n" + "\n" + "inline ::google::protobuf::RpcChannel* channel() { return channel_; }\n" + "\n" + "// implements $classname$ ------------------------------------------\n" + "\n"); + + GenerateMethodSignatures(NON_VIRTUAL, printer); + + printer->Outdent(); + printer->Print(vars_, + " private:\n" + " ::google::protobuf::RpcChannel* channel_;\n" + " bool owns_channel_;\n" + " GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($classname$_Stub);\n" + "};\n" + "\n"); +} + +void ServiceGenerator::GenerateMethodSignatures( + VirtualOrNon virtual_or_non, io::Printer* printer) { + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["name"] = method->name(); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + sub_vars["virtual"] = virtual_or_non == VIRTUAL ? "virtual " : ""; + + printer->Print(sub_vars, + "$virtual$void $name$(::google::protobuf::RpcController* controller,\n" + " const $input_type$* request,\n" + " $output_type$* response,\n" + " ::google::protobuf::Closure* done);\n"); + } +} + +// =================================================================== + +void ServiceGenerator::GenerateDescriptorInitializer( + io::Printer* printer, int index) { + map vars; + vars["classname"] = descriptor_->name(); + vars["index"] = SimpleItoa(index); + + printer->Print(vars, + "$classname$_descriptor_ = file->service($index$);\n"); +} + +// =================================================================== + +void ServiceGenerator::GenerateImplementation(io::Printer* printer) { + printer->Print(vars_, + "$classname$::~$classname$() {}\n" + "\n" + "const ::google::protobuf::ServiceDescriptor* $classname$::descriptor() {\n" + " return $classname$_descriptor_;\n" + "}\n" + "\n" + "const ::google::protobuf::ServiceDescriptor* $classname$::GetDescriptor() {\n" + " return $classname$_descriptor_;\n" + "}\n" + "\n"); + + // Generate methods of the interface. + GenerateNotImplementedMethods(printer); + GenerateCallMethod(printer); + GenerateGetPrototype(REQUEST, printer); + GenerateGetPrototype(RESPONSE, printer); + + // Generate stub implementation. + printer->Print(vars_, + "$classname$_Stub::$classname$_Stub(::google::protobuf::RpcChannel* channel)\n" + " : channel_(channel), owns_channel_(false) {}\n" + "$classname$_Stub::$classname$_Stub(\n" + " ::google::protobuf::RpcChannel* channel,\n" + " ::google::protobuf::Service::ChannelOwnership ownership)\n" + " : channel_(channel),\n" + " owns_channel_(ownership == ::google::protobuf::Service::STUB_OWNS_CHANNEL) {}\n" + "$classname$_Stub::~$classname$_Stub() {\n" + " if (owns_channel_) delete channel_;\n" + "}\n" + "\n"); + + GenerateStubMethods(printer); +} + +void ServiceGenerator::GenerateNotImplementedMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["classname"] = descriptor_->name(); + sub_vars["name"] = method->name(); + sub_vars["index"] = SimpleItoa(i); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + + printer->Print(sub_vars, + "void $classname$::$name$(::google::protobuf::RpcController* controller,\n" + " const $input_type$* request,\n" + " $output_type$* response,\n" + " ::google::protobuf::Closure* done) {\n" + " controller->SetFailed(\"Method $name$() not implemented.\");\n" + " done->Run();\n" + "}\n" + "\n"); + } +} + +void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { + printer->Print(vars_, + "void $classname$::CallMethod(const ::google::protobuf::MethodDescriptor* method,\n" + " ::google::protobuf::RpcController* controller,\n" + " const ::google::protobuf::Message* request,\n" + " ::google::protobuf::Message* response,\n" + " ::google::protobuf::Closure* done) {\n" + " GOOGLE_DCHECK_EQ(method->service(), $classname$_descriptor_);\n" + " switch(method->index()) {\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["name"] = method->name(); + sub_vars["index"] = SimpleItoa(i); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + + // Note: ::google::protobuf::down_cast does not work here because it only works on pointers, + // not references. + printer->Print(sub_vars, + " case $index$:\n" + " $name$(controller,\n" + " ::google::protobuf::down_cast(request),\n" + " ::google::protobuf::down_cast< $output_type$*>(response),\n" + " done);\n" + " break;\n"); + } + + printer->Print(vars_, + " default:\n" + " GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n" + " break;\n" + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, + io::Printer* printer) { + if (which == REQUEST) { + printer->Print(vars_, + "const ::google::protobuf::Message& $classname$::GetRequestPrototype(\n"); + } else { + printer->Print(vars_, + "const ::google::protobuf::Message& $classname$::GetResponsePrototype(\n"); + } + + printer->Print(vars_, + " const ::google::protobuf::MethodDescriptor* method) const {\n" + " GOOGLE_DCHECK_EQ(method->service(), $classname$_descriptor_);\n" + " switch(method->index()) {\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + const Descriptor* type = + (which == REQUEST) ? method->input_type() : method->output_type(); + + map sub_vars; + sub_vars["index"] = SimpleItoa(i); + sub_vars["type"] = ClassName(type, true); + + printer->Print(sub_vars, + " case $index$:\n" + " return $type$::default_instance();\n"); + } + + printer->Print(vars_, + " default:\n" + " GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n" + " return *reinterpret_cast< ::google::protobuf::Message*>(NULL);\n" + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateStubMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["classname"] = descriptor_->name(); + sub_vars["name"] = method->name(); + sub_vars["index"] = SimpleItoa(i); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + + printer->Print(sub_vars, + "void $classname$_Stub::$name$(::google::protobuf::RpcController* controller,\n" + " const $input_type$* request,\n" + " $output_type$* response,\n" + " ::google::protobuf::Closure* done) {\n" + " channel_->CallMethod($classname$_descriptor_->method($index$),\n" + " controller, request, response, done);\n" + "}\n"); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_service.h b/src/google/protobuf/compiler/cpp/cpp_service.h new file mode 100644 index 00000000..bccb64e4 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_service.h @@ -0,0 +1,104 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class ServiceGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit ServiceGenerator(const ServiceDescriptor* descriptor, + const string& dllexport_decl); + ~ServiceGenerator(); + + // Header stuff. + + // Generate the class definitions for the service's interface and the + // stub implementation. + void GenerateDeclarations(io::Printer* printer); + + // Source file stuff. + + // Generate code that initializes the global variable storing the service's + // descriptor. + void GenerateDescriptorInitializer(io::Printer* printer, int index); + + // Generate implementations of everything declared by GenerateDeclarations(). + void GenerateImplementation(io::Printer* printer); + + private: + enum RequestOrResponse { REQUEST, RESPONSE }; + enum VirtualOrNon { VIRTUAL, NON_VIRTUAL }; + + // Header stuff. + + // Generate the service abstract interface. + void GenerateInterface(io::Printer* printer); + + // Generate the stub class definition. + void GenerateStubDefinition(io::Printer* printer); + + // Prints signatures for all methods in the + void GenerateMethodSignatures(VirtualOrNon virtual_or_non, + io::Printer* printer); + + // Source file stuff. + + // Generate the default implementations of the service methods, which + // produce a "not implemented" error. + void GenerateNotImplementedMethods(io::Printer* printer); + + // Generate the CallMethod() method of the service. + void GenerateCallMethod(io::Printer* printer); + + // Generate the Get{Request,Response}Prototype() methods. + void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer); + + // Generate the stub's implementations of the service methods. + void GenerateStubMethods(io::Printer* printer); + + const ServiceDescriptor* descriptor_; + map vars_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc new file mode 100644 index 00000000..de59ac87 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -0,0 +1,336 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetStringVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = FieldName(descriptor); + (*variables)["default"] = + "\"" + CEscape(descriptor->default_value_string()) + "\""; + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type()); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); +} + +} // namespace + +// =================================================================== + +StringFieldGenerator:: +StringFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetStringVariables(descriptor, &variables_); +} + +StringFieldGenerator::~StringFieldGenerator() {} + +void StringFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::std::string* $name$_;\n" + "static const ::std::string _default_$name$_;\n"); +} + +void StringFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + // If we're using StringFieldGenerator for a field with a ctype, it's + // because that ctype isn't actually implemented. In particular, this is + // true of ctype=CORD and ctype=STRING_PIECE in the open source release. + // We aren't releasing Cord because it has too many Google-specific + // dependencies and we aren't releasing StringPiece because it's hardly + // useful outside of Google and because it would get confusing to have + // multiple instances of the StringPiece class in different libraries (PCRE + // already includes it for their C++ bindings, which came from Google). + // + // In any case, we make all the accessors private while still actually + // using a string to represent the field internally. This way, we can + // guarantee that if we do ever implement the ctype, it won't break any + // existing users who might be -- for whatever reason -- already using .proto + // files that applied the ctype. The field can still be accessed via the + // reflection interface since the reflection interface is independent of + // the string's underlying representation. + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print( + " private:\n" + " // Hidden due to unknown ctype option.\n"); + printer->Indent(); + } + + printer->Print(variables_, + "inline const ::std::string& $name$() const;\n" + "inline void set_$name$(const ::std::string& value);\n" + "inline void set_$name$(const char* value);\n"); + + printer->Print(variables_, + "inline ::std::string* mutable_$name$();\n"); + + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print(" public:\n"); + printer->Indent(); + } +} + +void StringFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::std::string& $classname$::$name$() const {\n" + " return *$name$_;\n" + "}\n" + "inline void $classname$::set_$name$(const ::std::string& value) {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n" + " $name$_ = new ::std::string;\n" + " }\n" + " $name$_->assign(value);\n" + "}\n" + "inline void $classname$::set_$name$(const char* value) {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n" + " $name$_ = new ::std::string;\n" + " }\n" + " $name$_->assign(value);\n" + "}\n"); + printer->Print(variables_, + "inline ::std::string* $classname$::mutable_$name$() {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n"); + if (descriptor_->has_default_value()) { + printer->Print(variables_, + " $name$_ = new ::std::string(_default_$name$_);\n"); + } else { + printer->Print(variables_, + " $name$_ = new ::std::string;\n"); + } + printer->Print(variables_, + " }\n" + " return $name$_;\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateNonInlineAccessorDefinitions(io::Printer* printer) const { + if (descriptor_->has_default_value()) { + printer->Print(variables_, + "const ::std::string $classname$::_default_$name$_($default$);"); + } else { + printer->Print(variables_, + "const ::std::string $classname$::_default_$name$_;"); + } +} + +void StringFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + if (descriptor_->has_default_value()) { + printer->Print(variables_, + "if ($name$_ != &_default_$name$_) {\n" + " $name$_->assign(_default_$name$_);\n" + "}\n"); + } else { + printer->Print(variables_, + "if ($name$_ != &_default_$name$_) {\n" + " $name$_->clear();\n" + "}\n"); + } +} + +void StringFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "set_$name$(from.$name$());\n"); +} + +void StringFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, + ",\n$name$_(const_cast< ::std::string*>(&_default_$name$_))"); +} + +void StringFieldGenerator:: +GenerateDestructorCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($name$_ != &_default_$name$_) {\n" + " delete $name$_;\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(" + "input, mutable_$name$()));\n"); +} + +void StringFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(), output));\n"); +} + +void StringFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::$declared_type$Size(this->$name$());\n"); +} + +// =================================================================== + +RepeatedStringFieldGenerator:: +RepeatedStringFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetStringVariables(descriptor, &variables_); +} + +RepeatedStringFieldGenerator::~RepeatedStringFieldGenerator() {} + +void RepeatedStringFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::google::protobuf::RepeatedPtrField< ::std::string> $name$_;\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + // See comment above about unknown ctypes. + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print( + " private:\n" + " // Hidden due to unknown ctype option.\n"); + printer->Indent(); + } + + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< ::std::string>& $name$() const;\n" + "inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_$name$();\n" + "inline const ::std::string& $name$(int index) const;\n" + "inline ::std::string* mutable_$name$(int index);\n" + "inline void set_$name$(int index, const ::std::string& value);\n" + "inline void set_$name$(int index, const char* value);\n" + "inline ::std::string* add_$name$();\n" + "inline void add_$name$(const ::std::string& value);\n" + "inline void add_$name$(const char* value);\n"); + + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print(" public:\n"); + printer->Indent(); + } +} + +void RepeatedStringFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< ::std::string>&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedPtrField< ::std::string>*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline const ::std::string& $classname$::$name$(int index) const {\n" + " return $name$_.Get(index);\n" + "}\n" + "inline ::std::string* $classname$::mutable_$name$(int index) {\n" + " return $name$_.Mutable(index);\n" + "}\n" + "inline void $classname$::set_$name$(int index, const ::std::string& value) {\n" + " $name$_.Mutable(index)->assign(value);\n" + "}\n" + "inline void $classname$::set_$name$(int index, const char* value) {\n" + " $name$_.Mutable(index)->assign(value);\n" + "}\n" + "inline ::std::string* $classname$::add_$name$() {\n" + " return $name$_.Add();\n" + "}\n" + "inline void $classname$::add_$name$(const ::std::string& value) {\n" + " $name$_.Add()->assign(value);\n" + "}\n" + "inline void $classname$::add_$name$(const char* value) {\n" + " $name$_.Add()->assign(value);\n" + "}\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedStringFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(\n" + " input, add_$name$()));\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size += ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" + " this->$name$(i));\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h new file mode 100644 index 00000000..44ffd18c --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -0,0 +1,86 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class StringFieldGenerator : public FieldGenerator { + public: + explicit StringFieldGenerator(const FieldDescriptor* descriptor); + ~StringFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateDestructorCode(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator); +}; + +class RepeatedStringFieldGenerator : public FieldGenerator { + public: + explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedStringFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedStringFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto new file mode 100644 index 00000000..ee0499bf --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -0,0 +1,87 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file tests that various identifiers work as field and type names even +// though the same identifiers are used internally by the C++ code generator. + + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +package protobuf_unittest; + +// Test that fields can have names like "input" and "i" which are also used +// internally by the code generator for local variables. +message TestConflictingSymbolNames { + message BuildDescriptors {} + message TypeTraits {} + + optional int32 input = 1; + optional int32 output = 2; + optional string length = 3; + repeated int32 i = 4; + repeated string new_element = 5 [ctype=STRING_PIECE]; + optional int32 total_size = 6; + optional int32 tag = 7; + + optional int32 source = 8; + optional int32 value = 9; + optional int32 file = 10; + optional int32 from = 11; + optional int32 handle_uninterpreted = 12; + repeated int32 index = 13; + optional int32 controller = 14; + optional int32 already_here = 15; + + optional uint32 uint32 = 16; + optional uint64 uint64 = 17; + optional string string = 18; + optional int32 memset = 19; + optional int32 int32 = 20; + optional int64 int64 = 21; + + optional uint32 cached_size = 22; + optional uint32 extensions = 23; + optional uint32 bit = 24; + optional uint32 bits = 25; + optional uint32 offsets = 26; + optional uint32 reflection = 27; + + message Cord {} + optional string some_cord = 28 [ctype=CORD]; + + message StringPiece {} + optional string some_string_piece = 29 [ctype=STRING_PIECE]; + + // Some keywords. + optional uint32 int = 30; + optional uint32 friend = 31; + + // The generator used to #define a macro called "DO" inside the .cc file. + message DO {} + optional DO do = 32; + + extensions 1000 to max; +} + +message DummyMessage {} + +service TestConflictingMethodNames { + rpc Closure(DummyMessage) returns (DummyMessage); +} diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc new file mode 100644 index 00000000..8253242b --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -0,0 +1,835 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// To test the code generator, we actually use it to generate code for +// google/protobuf/unittest.proto, then test that. This means that we +// are actually testing the parser and other parts of the system at the same +// time, and that problems in the generator may show up as compile-time errors +// rather than unittest failures, which may be surprising. However, testing +// the output of the C++ generator directly would be very hard. We can't very +// well just check it against golden files since those files would have to be +// updated for any small change; such a test would be very brittle and probably +// not very helpful. What we really want to test is that the code compiles +// correctly and produces the interfaces we expect, which is why this test +// is written this way. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + + +class MockErrorCollector : public MultiFileErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, int line, int column, + const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", + filename, line, column, message); + } +}; + +// Test that generated code has proper descriptors: +// Parse a descriptor directly (using google::protobuf::compiler::Importer) and +// compare it to the one that was produced by generated code. +TEST(GeneratedDescriptorTest, IdenticalDescriptors) { + const FileDescriptor* generated_descriptor = + unittest::TestAllTypes::descriptor()->file(); + + // Set up the Importer. + MockErrorCollector error_collector; + DiskSourceTree source_tree; + source_tree.MapPath("", TestSourceDir()); + Importer importer(&source_tree, &error_collector); + + // Import (parse) unittest.proto. + const FileDescriptor* parsed_descriptor = + importer.Import("google/protobuf/unittest.proto"); + EXPECT_EQ("", error_collector.text_); + ASSERT_TRUE(parsed_descriptor != NULL); + + // Test that descriptors are generated correctly by converting them to + // FileDescriptorProtos and comparing. + FileDescriptorProto generated_decsriptor_proto, parsed_descriptor_proto; + generated_descriptor->CopyTo(&generated_decsriptor_proto); + parsed_descriptor->CopyTo(&parsed_descriptor_proto); + + EXPECT_EQ(parsed_descriptor_proto.DebugString(), + generated_decsriptor_proto.DebugString()); +} + +// =================================================================== + +TEST(GeneratedMessageTest, Defaults) { + // Check that all default values are set correctly in the initial message. + unittest::TestAllTypes message; + + TestUtil::ExpectClear(message); + + // Messages should return pointers to default instances until first use. + // (This is not checked by ExpectClear() since it is not actually true after + // the fields have been set and then cleared.) + EXPECT_EQ(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &message.optionalgroup()); + EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.optional_nested_message()); + EXPECT_EQ(&unittest::ForeignMessage::default_instance(), + &message.optional_foreign_message()); + EXPECT_EQ(&unittest_import::ImportMessage::default_instance(), + &message.optional_import_message()); +} + +TEST(GeneratedMessageTest, Accessors) { + // Set every field to a unique value then go back and check all those + // values. + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + TestUtil::ExpectAllFieldsSet(message); + + TestUtil::ModifyRepeatedFields(&message); + TestUtil::ExpectRepeatedFieldsModified(message); +} + +TEST(GeneratedMessageTest, MutableStringDefault) { + // mutable_foo() for a string should return a string initialized to its + // default value. + unittest::TestAllTypes message; + + EXPECT_EQ("hello", *message.mutable_default_string()); + + // Note that the first time we call mutable_foo(), we get a newly-allocated + // string, but if we clear it and call it again, we get the same object again. + // We should verify that it has its default value in both cases. + message.set_default_string("blah"); + message.Clear(); + + EXPECT_EQ("hello", *message.mutable_default_string()); +} + +TEST(GeneratedMessageTest, Clear) { + // Set every field to a unique value, clear the message, then check that + // it is cleared. + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + message.Clear(); + TestUtil::ExpectClear(message); + + // Unlike with the defaults test, we do NOT expect that requesting embedded + // messages will return a pointer to the default instance. Instead, they + // should return the objects that were created when mutable_blah() was + // called. + EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &message.optionalgroup()); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.optional_nested_message()); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.optional_foreign_message()); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.optional_import_message()); +} + +TEST(GeneratedMessageTest, ClearOneField) { + // Set every field to a unique value, then clear one value and insure that + // only that one value is cleared. + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + int64 original_value = message.optional_int64(); + + // Clear the field and make sure it shows up as cleared. + message.clear_optional_int64(); + EXPECT_FALSE(message.has_optional_int64()); + EXPECT_EQ(0, message.optional_int64()); + + // Other adjacent fields should not be cleared. + EXPECT_TRUE(message.has_optional_int32()); + EXPECT_TRUE(message.has_optional_uint32()); + + // Make sure if we set it again, then all fields are set. + message.set_optional_int64(original_value); + TestUtil::ExpectAllFieldsSet(message); +} + + +TEST(GeneratedMessageTest, CopyFrom) { + unittest::TestAllTypes message1, message2; + string data; + + TestUtil::SetAllFields(&message1); + message2.CopyFrom(message1); + TestUtil::ExpectAllFieldsSet(message2); + + // Copying from self should be a no-op. + message2.CopyFrom(message2); + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, CopyConstructor) { + unittest::TestAllTypes message1; + TestUtil::SetAllFields(&message1); + + unittest::TestAllTypes message2(message1); + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, CopyAssignmentOperator) { + unittest::TestAllTypes message1; + TestUtil::SetAllFields(&message1); + + unittest::TestAllTypes message2; + message2 = message1; + TestUtil::ExpectAllFieldsSet(message2); + + // Make sure that self-assignment does something sane. + message2 = message2; + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, UpcastCopyFrom) { + // Test the CopyFrom method that takes in the generic const Message& + // parameter. + unittest::TestAllTypes message1, message2; + + TestUtil::SetAllFields(&message1); + + const Message* source = implicit_cast(&message1); + message2.CopyFrom(*source); + + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, DynamicMessageCopyFrom) { + // Test copying from a DynamicMessage, which must fall back to using + // reflection. + unittest::TestAllTypes message2; + + // Construct a new version of the dynamic message via the factory. + DynamicMessageFactory factory; + scoped_ptr message1; + message1.reset(factory.GetPrototype( + unittest::TestAllTypes::descriptor())->New()); + + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + reflection_tester.SetAllFieldsViaReflection(message1->GetReflection()); + + message2.CopyFrom(*message1); + + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, NonEmptyMergeFrom) { + // Test merging with a non-empty message. Code is a modified form + // of that found in google/protobuf/reflection_ops_unittest.cc. + unittest::TestAllTypes message1, message2; + + TestUtil::SetAllFields(&message1); + + // This field will test merging into an empty spot. + message2.set_optional_int32(message1.optional_int32()); + message1.clear_optional_int32(); + + // This tests overwriting. + message2.set_optional_string(message1.optional_string()); + message1.set_optional_string("something else"); + + // This tests concatenating. + message2.add_repeated_int32(message1.repeated_int32(1)); + int32 i = message1.repeated_int32(0); + message1.clear_repeated_int32(); + message1.add_repeated_int32(i); + + message1.MergeFrom(message2); + + TestUtil::ExpectAllFieldsSet(message1); +} + +#ifdef GTEST_HAS_DEATH_TEST + +TEST(GeneratedMessageTest, MergeFromSelf) { + unittest::TestAllTypes message; + EXPECT_DEATH(message.MergeFrom(message), "&from"); + EXPECT_DEATH(message.MergeFrom(implicit_cast(message)), + "&from"); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(GeneratedMessageTest, Serialization) { + unittest::TestAllTypes message1, message2; + string data; + + TestUtil::SetAllFields(&message1); + message1.SerializeToString(&data); + EXPECT_TRUE(message2.ParseFromString(data)); + TestUtil::ExpectAllFieldsSet(message2); + +} + + +TEST(GeneratedMessageTest, Required) { + // Test that IsInitialized() returns false if required fields are missing. + unittest::TestRequired message; + + EXPECT_FALSE(message.IsInitialized()); + message.set_a(1); + EXPECT_FALSE(message.IsInitialized()); + message.set_b(2); + EXPECT_FALSE(message.IsInitialized()); + message.set_c(3); + EXPECT_TRUE(message.IsInitialized()); +} + +TEST(GeneratedMessageTest, RequiredForeign) { + // Test that IsInitialized() returns false if required fields in nested + // messages are missing. + unittest::TestRequiredForeign message; + + EXPECT_TRUE(message.IsInitialized()); + + message.mutable_optional_message(); + EXPECT_FALSE(message.IsInitialized()); + + message.mutable_optional_message()->set_a(1); + message.mutable_optional_message()->set_b(2); + message.mutable_optional_message()->set_c(3); + EXPECT_TRUE(message.IsInitialized()); + + message.add_repeated_message(); + EXPECT_FALSE(message.IsInitialized()); + + message.mutable_repeated_message(0)->set_a(1); + message.mutable_repeated_message(0)->set_b(2); + message.mutable_repeated_message(0)->set_c(3); + EXPECT_TRUE(message.IsInitialized()); +} + +TEST(GeneratedMessageTest, ForeignNested) { + // Test that TestAllTypes::NestedMessage can be embedded directly into + // another message. + unittest::TestForeignNested message; + + // If this compiles and runs without crashing, it must work. We have + // nothing more to test. + unittest::TestAllTypes::NestedMessage* nested = + message.mutable_foreign_nested(); + nested->set_bb(1); +} + +TEST(GeneratedMessageTest, ReallyLargeTagNumber) { + // Test that really large tag numbers don't break anything. + unittest::TestReallyLargeTagNumber message1, message2; + string data; + + // For the most part, if this compiles and runs then we're probably good. + // (The most likely cause for failure would be if something were attempting + // to allocate a lookup table of some sort using tag numbers as the index.) + // We'll try serializing just for fun. + message1.set_a(1234); + message1.set_bb(5678); + message1.SerializeToString(&data); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(1234, message2.a()); + EXPECT_EQ(5678, message2.bb()); +} + +TEST(GeneratedMessageTest, MutualRecursion) { + // Test that mutually-recursive message types work. + unittest::TestMutualRecursionA message; + unittest::TestMutualRecursionA* nested = message.mutable_bb()->mutable_a(); + unittest::TestMutualRecursionA* nested2 = nested->mutable_bb()->mutable_a(); + + // Again, if the above compiles and runs, that's all we really have to + // test, but just for run we'll check that the system didn't somehow come + // up with a pointer loop... + EXPECT_NE(&message, nested); + EXPECT_NE(&message, nested2); + EXPECT_NE(nested, nested2); +} + +TEST(GeneratedMessageTest, CamelCaseFieldNames) { + // This test is mainly checking that the following compiles, which verifies + // that the field names were coerced to lower-case. + // + // Protocol buffers standard style is to use lowercase-with-underscores for + // field names. Some old proto1 .protos unfortunately used camel-case field + // names. In proto1, these names were forced to lower-case. So, we do the + // same thing in proto2. + + unittest::TestCamelCaseFieldNames message; + + message.set_primitivefield(2); + message.set_stringfield("foo"); + message.set_enumfield(unittest::FOREIGN_FOO); + message.mutable_messagefield()->set_c(6); + + message.add_repeatedprimitivefield(8); + message.add_repeatedstringfield("qux"); + message.add_repeatedenumfield(unittest::FOREIGN_BAR); + message.add_repeatedmessagefield()->set_c(15); + + EXPECT_EQ(2, message.primitivefield()); + EXPECT_EQ("foo", message.stringfield()); + EXPECT_EQ(unittest::FOREIGN_FOO, message.enumfield()); + EXPECT_EQ(6, message.messagefield().c()); + + EXPECT_EQ(8, message.repeatedprimitivefield(0)); + EXPECT_EQ("qux", message.repeatedstringfield(0)); + EXPECT_EQ(unittest::FOREIGN_BAR, message.repeatedenumfield(0)); + EXPECT_EQ(15, message.repeatedmessagefield(0).c()); +} + +TEST(GeneratedMessageTest, TestConflictingSymbolNames) { + // test_bad_identifiers.proto successfully compiled, then it works. The + // following is just a token usage to insure that the code is, in fact, + // being compiled and linked. + + protobuf_unittest::TestConflictingSymbolNames message; + message.set_uint32(1); + EXPECT_EQ(3, message.ByteSize()); + + message.set_friend_(5); + EXPECT_EQ(5, message.friend_()); +} + +TEST(GeneratedMessageTest, TestOptimizedForSize) { + // We rely on the tests in reflection_ops_unittest and wire_format_unittest + // to really test that reflection-based methods work. Here we are mostly + // just making sure that TestOptimizedForSize actually builds and seems to + // function. + + protobuf_unittest::TestOptimizedForSize message, message2; + message.set_i(1); + message.mutable_msg()->set_c(2); + message2.CopyFrom(message); + EXPECT_EQ(1, message2.i()); + EXPECT_EQ(2, message2.msg().c()); +} + +TEST(GeneratedMessageTest, TestEmbedOptimizedForSize) { + // Verifies that something optimized for speed can contain something optimized + // for size. + + protobuf_unittest::TestEmbedOptimizedForSize message, message2; + message.mutable_optional_message()->set_i(1); + message.add_repeated_message()->mutable_msg()->set_c(2); + string data; + message.SerializeToString(&data); + ASSERT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(1, message2.optional_message().i()); + EXPECT_EQ(2, message2.repeated_message(0).msg().c()); +} + +// =================================================================== + +TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) { + // Test that our nested enum values can be used as switch cases. This test + // doesn't actually do anything, the proof that it works is that it + // compiles. + int i =0; + unittest::TestAllTypes::NestedEnum a = unittest::TestAllTypes::BAR; + switch (a) { + case unittest::TestAllTypes::FOO: + i = 1; + break; + case unittest::TestAllTypes::BAR: + i = 2; + break; + case unittest::TestAllTypes::BAZ: + i = 3; + break; + // no default case: We want to make sure the compiler recognizes that + // all cases are covered. (GCC warns if you do not cover all cases of + // an enum in a switch.) + } + + // Token check just for fun. + EXPECT_EQ(2, i); +} + +TEST(GeneratedEnumTest, IsValidValue) { + // Test enum IsValidValue. + EXPECT_TRUE(unittest::TestAllTypes::NestedEnum_IsValid(1)); + EXPECT_TRUE(unittest::TestAllTypes::NestedEnum_IsValid(2)); + EXPECT_TRUE(unittest::TestAllTypes::NestedEnum_IsValid(3)); + + EXPECT_FALSE(unittest::TestAllTypes::NestedEnum_IsValid(0)); + EXPECT_FALSE(unittest::TestAllTypes::NestedEnum_IsValid(4)); + + // Make sure it also works when there are dups. + EXPECT_TRUE(unittest::TestEnumWithDupValue_IsValid(1)); + EXPECT_TRUE(unittest::TestEnumWithDupValue_IsValid(2)); + EXPECT_TRUE(unittest::TestEnumWithDupValue_IsValid(3)); + + EXPECT_FALSE(unittest::TestEnumWithDupValue_IsValid(0)); + EXPECT_FALSE(unittest::TestEnumWithDupValue_IsValid(4)); +} + +TEST(GeneratedEnumTest, MinAndMax) { + EXPECT_EQ(unittest::TestAllTypes::FOO,unittest::TestAllTypes::NestedEnum_MIN); + EXPECT_EQ(unittest::TestAllTypes::BAZ,unittest::TestAllTypes::NestedEnum_MAX); + + EXPECT_EQ(unittest::FOREIGN_FOO, unittest::ForeignEnum_MIN); + EXPECT_EQ(unittest::FOREIGN_BAZ, unittest::ForeignEnum_MAX); + + EXPECT_EQ(1, unittest::TestEnumWithDupValue_MIN); + EXPECT_EQ(3, unittest::TestEnumWithDupValue_MAX); + + EXPECT_EQ(unittest::SPARSE_E, unittest::TestSparseEnum_MIN); + EXPECT_EQ(unittest::SPARSE_C, unittest::TestSparseEnum_MAX); + + // Make sure we can use _MIN and _MAX as switch cases. + switch(unittest::SPARSE_A) { + case unittest::TestSparseEnum_MIN: + case unittest::TestSparseEnum_MAX: + break; + default: + break; + } +} + +// =================================================================== + +// Support code for testing services. +class GeneratedServiceTest : public testing::Test { + protected: + class MockTestService : public unittest::TestService { + public: + MockTestService() + : called_(false), + method_(""), + controller_(NULL), + request_(NULL), + response_(NULL), + done_(NULL) {} + + ~MockTestService() {} + + void Reset() { called_ = false; } + + // implements TestService ---------------------------------------- + + void Foo(RpcController* controller, + const unittest::FooRequest* request, + unittest::FooResponse* response, + Closure* done) { + ASSERT_FALSE(called_); + called_ = true; + method_ = "Foo"; + controller_ = controller; + request_ = request; + response_ = response; + done_ = done; + } + + void Bar(RpcController* controller, + const unittest::BarRequest* request, + unittest::BarResponse* response, + Closure* done) { + ASSERT_FALSE(called_); + called_ = true; + method_ = "Bar"; + controller_ = controller; + request_ = request; + response_ = response; + done_ = done; + } + + // --------------------------------------------------------------- + + bool called_; + string method_; + RpcController* controller_; + const Message* request_; + Message* response_; + Closure* done_; + }; + + class MockRpcChannel : public RpcChannel { + public: + MockRpcChannel() + : called_(false), + method_(NULL), + controller_(NULL), + request_(NULL), + response_(NULL), + done_(NULL), + destroyed_(NULL) {} + + ~MockRpcChannel() { + if (destroyed_ != NULL) *destroyed_ = true; + } + + void Reset() { called_ = false; } + + // implements TestService ---------------------------------------- + + void CallMethod(const MethodDescriptor* method, + RpcController* controller, + const Message* request, + Message* response, + Closure* done) { + ASSERT_FALSE(called_); + called_ = true; + method_ = method; + controller_ = controller; + request_ = request; + response_ = response; + done_ = done; + } + + // --------------------------------------------------------------- + + bool called_; + const MethodDescriptor* method_; + RpcController* controller_; + const Message* request_; + Message* response_; + Closure* done_; + bool* destroyed_; + }; + + class MockController : public RpcController { + public: + void Reset() { + ADD_FAILURE() << "Reset() not expected during this test."; + } + bool Failed() const { + ADD_FAILURE() << "Failed() not expected during this test."; + return false; + } + string ErrorText() const { + ADD_FAILURE() << "ErrorText() not expected during this test."; + return ""; + } + void StartCancel() { + ADD_FAILURE() << "StartCancel() not expected during this test."; + } + void SetFailed(const string& reason) { + ADD_FAILURE() << "SetFailed() not expected during this test."; + } + bool IsCanceled() const { + ADD_FAILURE() << "IsCanceled() not expected during this test."; + return false; + } + void NotifyOnCancel(Closure* callback) { + ADD_FAILURE() << "NotifyOnCancel() not expected during this test."; + } + }; + + GeneratedServiceTest() + : descriptor_(unittest::TestService::descriptor()), + foo_(descriptor_->FindMethodByName("Foo")), + bar_(descriptor_->FindMethodByName("Bar")), + stub_(&mock_channel_), + done_(NewPermanentCallback(&DoNothing)) {} + + virtual void SetUp() { + ASSERT_TRUE(foo_ != NULL); + ASSERT_TRUE(bar_ != NULL); + } + + const ServiceDescriptor* descriptor_; + const MethodDescriptor* foo_; + const MethodDescriptor* bar_; + + MockTestService mock_service_; + MockController mock_controller_; + + MockRpcChannel mock_channel_; + unittest::TestService::Stub stub_; + + // Just so we don't have to re-define these with every test. + unittest::FooRequest foo_request_; + unittest::FooResponse foo_response_; + unittest::BarRequest bar_request_; + unittest::BarResponse bar_response_; + scoped_ptr done_; +}; + +TEST_F(GeneratedServiceTest, GetDescriptor) { + // Test that GetDescriptor() works. + + EXPECT_EQ(descriptor_, mock_service_.GetDescriptor()); +} + +TEST_F(GeneratedServiceTest, GetChannel) { + EXPECT_EQ(&mock_channel_, stub_.channel()); +} + +TEST_F(GeneratedServiceTest, OwnsChannel) { + MockRpcChannel* channel = new MockRpcChannel; + bool destroyed = false; + channel->destroyed_ = &destroyed; + + { + unittest::TestService::Stub owning_stub(channel, + Service::STUB_OWNS_CHANNEL); + EXPECT_FALSE(destroyed); + } + + EXPECT_TRUE(destroyed); +} + +TEST_F(GeneratedServiceTest, CallMethod) { + // Test that CallMethod() works. + + // Call Foo() via CallMethod(). + mock_service_.CallMethod(foo_, &mock_controller_, + &foo_request_, &foo_response_, done_.get()); + + ASSERT_TRUE(mock_service_.called_); + + EXPECT_EQ("Foo" , mock_service_.method_ ); + EXPECT_EQ(&mock_controller_, mock_service_.controller_); + EXPECT_EQ(&foo_request_ , mock_service_.request_ ); + EXPECT_EQ(&foo_response_ , mock_service_.response_ ); + EXPECT_EQ(done_.get() , mock_service_.done_ ); + + // Try again, but call Bar() instead. + mock_service_.Reset(); + mock_service_.CallMethod(bar_, &mock_controller_, + &bar_request_, &bar_response_, done_.get()); + + ASSERT_TRUE(mock_service_.called_); + EXPECT_EQ("Bar", mock_service_.method_); +} + +TEST_F(GeneratedServiceTest, CallMethodTypeFailure) { + // Verify death if we call Foo() with Bar's message types. + +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + EXPECT_DEBUG_DEATH( + mock_service_.CallMethod(foo_, &mock_controller_, + &foo_request_, &bar_response_, done_.get()), + "dynamic_cast"); + + mock_service_.Reset(); + EXPECT_DEBUG_DEATH( + mock_service_.CallMethod(foo_, &mock_controller_, + &bar_request_, &foo_response_, done_.get()), + "dynamic_cast"); +#endif // GTEST_HAS_DEATH_TEST +} + +TEST_F(GeneratedServiceTest, GetPrototypes) { + // Test Get{Request,Response}Prototype() methods. + + EXPECT_EQ(&unittest::FooRequest::default_instance(), + &mock_service_.GetRequestPrototype(foo_)); + EXPECT_EQ(&unittest::BarRequest::default_instance(), + &mock_service_.GetRequestPrototype(bar_)); + + EXPECT_EQ(&unittest::FooResponse::default_instance(), + &mock_service_.GetResponsePrototype(foo_)); + EXPECT_EQ(&unittest::BarResponse::default_instance(), + &mock_service_.GetResponsePrototype(bar_)); +} + +TEST_F(GeneratedServiceTest, Stub) { + // Test that the stub class works. + + // Call Foo() via the stub. + stub_.Foo(&mock_controller_, &foo_request_, &foo_response_, done_.get()); + + ASSERT_TRUE(mock_channel_.called_); + + EXPECT_EQ(foo_ , mock_channel_.method_ ); + EXPECT_EQ(&mock_controller_, mock_channel_.controller_); + EXPECT_EQ(&foo_request_ , mock_channel_.request_ ); + EXPECT_EQ(&foo_response_ , mock_channel_.response_ ); + EXPECT_EQ(done_.get() , mock_channel_.done_ ); + + // Call Bar() via the stub. + mock_channel_.Reset(); + stub_.Bar(&mock_controller_, &bar_request_, &bar_response_, done_.get()); + + ASSERT_TRUE(mock_channel_.called_); + EXPECT_EQ(bar_, mock_channel_.method_); +} + +TEST_F(GeneratedServiceTest, NotImplemented) { + // Test that failing to implement a method of a service causes it to fail + // with a "not implemented" error message. + + // A service which doesn't implement any methods. + class UnimplementedService : public unittest::TestService { + public: + UnimplementedService() {} + }; + + UnimplementedService unimplemented_service; + + // And a controller which expects to get a "not implemented" error. + class ExpectUnimplementedController : public MockController { + public: + ExpectUnimplementedController() : called_(false) {} + + void SetFailed(const string& reason) { + EXPECT_FALSE(called_); + called_ = true; + EXPECT_EQ("Method Foo() not implemented.", reason); + } + + bool called_; + }; + + ExpectUnimplementedController controller; + + // Call Foo. + unimplemented_service.Foo(&controller, &foo_request_, &foo_response_, + done_.get()); + + EXPECT_TRUE(controller.called_); +} + +} // namespace + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc new file mode 100644 index 00000000..61182933 --- /dev/null +++ b/src/google/protobuf/compiler/importer.cc @@ -0,0 +1,398 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +#ifdef _WIN32 +#ifndef F_OK +#define F_OK 00 // not defined by MSVC for whatever reason +#endif +#endif + +MultiFileErrorCollector::~MultiFileErrorCollector() {} + +// This class serves two purposes: +// - It implements the ErrorCollector interface (used by Tokenizer and Parser) +// in terms of MultiFileErrorCollector, using a particular filename. +// - It lets us check if any errors have occurred. +class SourceTreeDescriptorDatabase::SingleFileErrorCollector + : public io::ErrorCollector { + public: + SingleFileErrorCollector(const string& filename, + MultiFileErrorCollector* multi_file_error_collector) + : filename_(filename), + multi_file_error_collector_(multi_file_error_collector), + had_errors_(false) {} + ~SingleFileErrorCollector() {} + + bool had_errors() { return had_errors_; } + + // implements ErrorCollector --------------------------------------- + void AddError(int line, int column, const string& message) { + if (multi_file_error_collector_ != NULL) { + multi_file_error_collector_->AddError(filename_, line, column, message); + } + had_errors_ = true; + } + + private: + string filename_; + MultiFileErrorCollector* multi_file_error_collector_; + bool had_errors_; +}; + +// =================================================================== + +SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase( + SourceTree* source_tree) + : source_tree_(source_tree), + error_collector_(NULL), + using_validation_error_collector_(false), + validation_error_collector_(this) {} + +SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {} + +bool SourceTreeDescriptorDatabase::FindFileByName( + const string& filename, FileDescriptorProto* output) { + scoped_ptr input(source_tree_->Open(filename)); + if (input == NULL) { + if (error_collector_ != NULL) { + error_collector_->AddError(filename, -1, 0, "File not found."); + } + return false; + } + + // Set up the tokenizer and parser. + SingleFileErrorCollector file_error_collector(filename, error_collector_); + io::Tokenizer tokenizer(input.get(), &file_error_collector); + + Parser parser; + if (error_collector_ != NULL) { + parser.RecordErrorsTo(&file_error_collector); + } + if (using_validation_error_collector_) { + parser.RecordSourceLocationsTo(&source_locations_); + } + + // Parse it. + output->set_name(filename); + return parser.Parse(&tokenizer, output) && + !file_error_collector.had_errors(); +} + +bool SourceTreeDescriptorDatabase::FindFileContainingSymbol( + const string& symbol_name, FileDescriptorProto* output) { + return false; +} + +bool SourceTreeDescriptorDatabase::FindFileContainingExtension( + const string& containing_type, int field_number, + FileDescriptorProto* output) { + return false; +} + +// ------------------------------------------------------------------- + +SourceTreeDescriptorDatabase::ValidationErrorCollector:: +ValidationErrorCollector(SourceTreeDescriptorDatabase* owner) + : owner_(owner) {} + +SourceTreeDescriptorDatabase::ValidationErrorCollector:: +~ValidationErrorCollector() {} + +void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError( + const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message) { + if (owner_->error_collector_ == NULL) return; + + int line, column; + owner_->source_locations_.Find(descriptor, location, &line, &column); + owner_->error_collector_->AddError(filename, line, column, message); +} + +// =================================================================== + +Importer::Importer(SourceTree* source_tree, + MultiFileErrorCollector* error_collector) + : database_(source_tree), + pool_(&database_, database_.GetValidationErrorCollector()) { + database_.RecordErrorsTo(error_collector); +} + +Importer::~Importer() {} + +const FileDescriptor* Importer::Import(const string& filename) { + return pool_.FindFileByName(filename); +} + +// =================================================================== + +SourceTree::~SourceTree() {} + +DiskSourceTree::DiskSourceTree() {} + +DiskSourceTree::~DiskSourceTree() {} + +static inline char LastChar(const string& str) { + return str[str.size() - 1]; +} + +// Given a path, returns an equivalent path with these changes: +// - On Windows, any backslashes are replaced with forward slashes. +// - Any instances of the directory "." are removed. +// - Any consecutive '/'s are collapsed into a single slash. +// Note that the resulting string may be empty. +// +// TODO(kenton): It would be nice to handle "..", e.g. so that we can figure +// out that "foo/bar.proto" is inside "baz/../foo". However, if baz is a +// symlink or doesn't exist, then things get complicated, and we can't +// actually determine this without investigating the filesystem, probably +// in non-portable ways. So, we punt. +// +// TODO(kenton): It would be nice to use realpath() here except that it +// resolves symbolic links. This could cause problems if people place +// symbolic links in their source tree. For example, if you executed: +// protoc --proto_path=foo foo/bar/baz.proto +// then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize +// to a path which does not appear to be under foo, and thus the compiler +// will complain that baz.proto is not inside the --proto_path. +static string CanonicalizePath(string path) { +#ifdef _WIN32 + // The Win32 API accepts forward slashes as a path delimiter even though + // backslashes are standard. Let's avoid confusion and use only forward + // slashes. + path = StringReplace(path, "\\", "/", true); +#endif + + vector parts; + vector canonical_parts; + SplitStringUsing(path, "/", &parts); // Note: Removes empty parts. + for (int i = 0; i < parts.size(); i++) { + if (parts[i] == ".") { + // Ignore. + } else { + canonical_parts.push_back(parts[i]); + } + } + string result = JoinStrings(canonical_parts, "/"); + if (!path.empty() && path[0] == '/') { + // Restore leading slash. + result = '/' + result; + } + if (!path.empty() && LastChar(path) == '/' && + !result.empty() && LastChar(result) != '/') { + // Restore trailing slash. + result += '/'; + } + return result; +} + +static inline bool ContainsParentReference(const string& path) { + return path == ".." || + HasPrefixString(path, "../") || + HasSuffixString(path, "/..") || + path.find("/../") != string::npos; +} + +// Maps a file from an old location to a new one. Typically, old_prefix is +// a virtual path and new_prefix is its corresponding disk path. Returns +// false if the filename did not start with old_prefix, otherwise replaces +// old_prefix with new_prefix and stores the result in *result. Examples: +// string result; +// assert(ApplyMapping("foo/bar", "", "baz", &result)); +// assert(result == "baz/foo/bar"); +// +// assert(ApplyMapping("foo/bar", "foo", "baz", &result)); +// assert(result == "baz/bar"); +// +// assert(ApplyMapping("foo", "foo", "bar", &result)); +// assert(result == "bar"); +// +// assert(!ApplyMapping("foo/bar", "baz", "qux", &result)); +// assert(!ApplyMapping("foo/bar", "baz", "qux", &result)); +// assert(!ApplyMapping("foobar", "foo", "baz", &result)); +static bool ApplyMapping(const string& filename, + const string& old_prefix, + const string& new_prefix, + string* result) { + if (old_prefix.empty()) { + // old_prefix matches any relative path. + if (ContainsParentReference(filename)) { + // We do not allow the file name to use "..". + return false; + } + if (HasPrefixString(filename, "/")) { + // This is an absolute path, so it isn't matched by the empty string. + return false; + } + result->assign(new_prefix); + if (!result->empty()) result->push_back('/'); + result->append(filename); + return true; + } else if (HasPrefixString(filename, old_prefix)) { + // old_prefix is a prefix of the filename. Is it the whole filename? + if (filename.size() == old_prefix.size()) { + // Yep, it's an exact match. + *result = new_prefix; + return true; + } else { + // Not an exact match. Is the next character a '/'? Otherwise, + // this isn't actually a match at all. E.g. the prefix "foo/bar" + // does not match the filename "foo/barbaz". + if (filename[old_prefix.size()] == '/') { + // Yep. So the prefixes are directories and the filename is a file + // inside them. + string after_prefix = filename.substr(old_prefix.size() + 1); + if (ContainsParentReference(after_prefix)) { + // We do not allow the file name to use "..". + return false; + } + result->assign(new_prefix); + if (!result->empty()) result->push_back('/'); + result->append(after_prefix); + return true; + } + } + } + + return false; +} + +void DiskSourceTree::MapPath(const string& virtual_path, + const string& disk_path) { + mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path))); +} + +DiskSourceTree::DiskFileToVirtualFileResult +DiskSourceTree::DiskFileToVirtualFile( + const string& disk_file, + string* virtual_file, + string* shadowing_disk_file) { + int mapping_index = -1; + string canonical_disk_file = CanonicalizePath(disk_file); + + for (int i = 0; i < mappings_.size(); i++) { + // Apply the mapping in reverse. + if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path, + mappings_[i].virtual_path, virtual_file)) { + // Success. + mapping_index = i; + break; + } + } + + if (mapping_index == -1) { + return NO_MAPPING; + } + + // Iterate through all mappings with higher precedence and verify that none + // of them map this file to some other existing file. + for (int i = 0; i < mapping_index; i++) { + if (ApplyMapping(*virtual_file, mappings_[i].virtual_path, + mappings_[i].disk_path, shadowing_disk_file)) { + if (access(shadowing_disk_file->c_str(), F_OK) >= 0) { + // File exists. + return SHADOWED; + } + } + } + shadowing_disk_file->clear(); + + // Verify that we can open the file. Note that this also has the side-effect + // of verifying that we are not canonicalizing away any non-existent + // directories. + scoped_ptr stream(OpenDiskFile(disk_file)); + if (stream == NULL) { + return CANNOT_OPEN; + } + + return SUCCESS; +} + +io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) { + if (filename != CanonicalizePath(filename) || + ContainsParentReference(filename)) { + // We do not allow importing of paths containing things like ".." or + // consecutive slashes since the compiler expects files to be uniquely + // identified by file name. + return NULL; + } + + for (int i = 0; i < mappings_.size(); i++) { + string disk_file; + if (ApplyMapping(filename, mappings_[i].virtual_path, + mappings_[i].disk_path, &disk_file)) { + io::ZeroCopyInputStream* stream = OpenDiskFile(disk_file); + if (stream != NULL) return stream; + + if (errno == EACCES) { + // The file exists but is not readable. + // TODO(kenton): Find a way to report this more nicely. + GOOGLE_LOG(WARNING) << "Read access is denied for file: " << disk_file; + return NULL; + } + } + } + + return NULL; +} + +io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile( + const string& filename) { + int file_descriptor; + do { + file_descriptor = open(filename.c_str(), O_RDONLY); + } while (file_descriptor < 0 && errno == EINTR); + if (file_descriptor >= 0) { + io::FileInputStream* result = new io::FileInputStream(file_descriptor); + result->SetCloseOnDelete(true); + return result; + } else { + return NULL; + } +} + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h new file mode 100644 index 00000000..2e3d0df4 --- /dev/null +++ b/src/google/protobuf/compiler/importer.h @@ -0,0 +1,279 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file is the public interface to the .proto file parser. + +#ifndef GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ +#define GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace io { class ZeroCopyInputStream; } + +namespace compiler { + +// Defined in this file. +class Importer; +class MultiFileErrorCollector; +class SourceTree; +class DiskSourceTree; + +// TODO(kenton): Move all SourceTree stuff to a separate file? + +// An implementation of DescriptorDatabase which loads files from a SourceTree +// and parses them. +// +// Note: This class is not thread-safe since it maintains a table of source +// code locations for error reporting. However, when a DescriptorPool wraps +// a DescriptorDatabase, it uses mutex locking to make sure only one method +// of the database is called at a time, even if the DescriptorPool is used +// from multiple threads. Therefore, there is only a problem if you create +// multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase +// and use them from multiple threads. +// +// Note: This class does not implement FindFileContainingSymbol() or +// FindFileContainingExtension(); these will always return false. +class LIBPROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase { + public: + SourceTreeDescriptorDatabase(SourceTree* source_tree); + ~SourceTreeDescriptorDatabase(); + + // Instructs the SourceTreeDescriptorDatabase to report any parse errors + // to the given MultiFileErrorCollector. This should be called before + // parsing. error_collector must remain valid until either this method + // is called again or the SourceTreeDescriptorDatabase is destroyed. + void RecordErrorsTo(MultiFileErrorCollector* error_collector) { + error_collector_ = error_collector; + } + + // Gets a DescriptorPool::ErrorCollector which records errors to the + // MultiFileErrorCollector specified with RecordErrorsTo(). This collector + // has the ability to determine exact line and column numbers of errors + // from the information given to it by the DescriptorPool. + DescriptorPool::ErrorCollector* GetValidationErrorCollector() { + using_validation_error_collector_ = true; + return &validation_error_collector_; + } + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + class SingleFileErrorCollector; + + SourceTree* source_tree_; + MultiFileErrorCollector* error_collector_; + + class LIBPROTOBUF_EXPORT ValidationErrorCollector : public DescriptorPool::ErrorCollector { + public: + ValidationErrorCollector(SourceTreeDescriptorDatabase* owner); + ~ValidationErrorCollector(); + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message); + + private: + SourceTreeDescriptorDatabase* owner_; + }; + friend class ValidationErrorCollector; + + bool using_validation_error_collector_; + SourceLocationTable source_locations_; + ValidationErrorCollector validation_error_collector_; +}; + +// Simple interface for parsing .proto files. This wraps the process +// of opening the file, parsing it with a Parser, recursively parsing all its +// imports, and then cross-linking the results to produce a FileDescriptor. +// +// This is really just a thin wrapper around SourceTreeDescriptorDatabase. +// You may find that SourceTreeDescriptorDatabase is more flexible. +// +// TODO(kenton): I feel like this class is not well-named. +class LIBPROTOBUF_EXPORT Importer { + public: + Importer(SourceTree* source_tree, + MultiFileErrorCollector* error_collector); + ~Importer(); + + // Import the given file and build a FileDescriptor representing it. If + // the file is already in the DescriptorPool, the existing FileDescriptor + // will be returned. The FileDescriptor is property of the DescriptorPool, + // and will remain valid until it is destroyed. If any errors occur, they + // will be reported using the error collector and Import() will return NULL. + // + // A particular Importer object will only report errors for a particular + // file once. All future attempts to import the same file will return NULL + // without reporting any errors. The idea is that you might want to import + // a lot of files without seeing the same errors over and over again. If + // you want to see errors for the same files repeatedly, you can use a + // separate Importer object to import each one (but use the same + // DescriptorPool so that they can be cross-linked). + const FileDescriptor* Import(const string& filename); + + // The DescriptorPool in which all imported FileDescriptors and their + // contents are stored. + inline const DescriptorPool* pool() const { + return &pool_; + } + + private: + SourceTreeDescriptorDatabase database_; + DescriptorPool pool_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer); +}; + +// If the importer encounters problems while trying to import the proto files, +// it reports them to a MultiFileErrorCollector. +class LIBPROTOBUF_EXPORT MultiFileErrorCollector { + public: + inline MultiFileErrorCollector() {} + virtual ~MultiFileErrorCollector(); + + // Line and column numbers are zero-based. A line number of -1 indicates + // an error with the entire file (e.g. "not found"). + virtual void AddError(const string& filename, int line, int column, + const string& message) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector); +}; + +// Abstract interface which represents a directory tree containing proto files. +// Used by the default implementation of Importer to resolve import statements +// Most users will probably want to use the DiskSourceTree implementation, +// below. +class LIBPROTOBUF_EXPORT SourceTree { + public: + inline SourceTree() {} + virtual ~SourceTree(); + + // Open the given file and return a stream that reads it, or NULL if not + // found. The caller takes ownership of the returned object. The filename + // must be a path relative to the root of the source tree and must not + // contain "." or ".." components. + virtual io::ZeroCopyInputStream* Open(const string& filename) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree); +}; + +// An implementation of SourceTree which loads files from locations on disk. +// Multiple mappings can be set up to map locations in the DiskSourceTree to +// locations in the physical filesystem. +class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { + public: + DiskSourceTree(); + ~DiskSourceTree(); + + // Map a path on disk to a location in the SourceTree. The path may be + // either a file or a directory. If it is a directory, the entire tree + // under it will be mapped to the given virtual location. To map a directory + // to the root of the source tree, pass an empty string for virtual_path. + // + // If multiple mapped paths apply when opening a file, they will be searched + // in order. For example, if you do: + // MapPath("bar", "foo/bar"); + // MapPath("", "baz"); + // and then you do: + // Open("bar/qux"); + // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux, + // returning the first one that opens successfuly. + // + // disk_path may be an absolute path or relative to the current directory, + // just like a path you'd pass to open(). + void MapPath(const string& virtual_path, const string& disk_path); + + // Return type for DiskFileToVirtualFile(). + enum DiskFileToVirtualFileResult { + SUCCESS, + SHADOWED, + CANNOT_OPEN, + NO_MAPPING + }; + + // Given a path to a file on disk, find a virtual path mapping to that + // file. The first mapping created with MapPath() whose disk_path contains + // the filename is used. However, that virtual path may not actually be + // usable to open the given file. Possible return values are: + // * SUCCESS: The mapping was found. *virtual_file is filled in so that + // calling Open(*virtual_file) will open the file named by disk_file. + // * SHADOWED: A mapping was found, but using Open() to open this virtual + // path will end up returning some different file. This is because some + // other mapping with a higher precedence also matches this virtual path + // and maps it to a different file that exists on disk. *virtual_file + // is filled in as it would be in the SUCCESS case. *shadowing_disk_file + // is filled in with the disk path of the file which would be opened if + // you were to call Open(*virtual_file). + // * CANNOT_OPEN: The mapping was found and was not shadowed, but the + // file specified cannot be opened. When this value is returned, + // errno will indicate the reason the file cannot be opened. *virtual_file + // will be set to the virtual path as in the SUCCESS case, even though + // it is not useful. + // * NO_MAPPING: Indicates that no mapping was found which contains this + // file. + DiskFileToVirtualFileResult + DiskFileToVirtualFile(const string& disk_file, + string* virtual_file, + string* shadowing_disk_file); + + // implements SourceTree ------------------------------------------- + io::ZeroCopyInputStream* Open(const string& filename); + + private: + struct Mapping { + string virtual_path; + string disk_path; + + inline Mapping(const string& virtual_path, const string& disk_path) + : virtual_path(virtual_path), disk_path(disk_path) {} + }; + vector mappings_; + + // Like Open() but given the actual on-disk path. + io::ZeroCopyInputStream* OpenDiskFile(const string& filename); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DiskSourceTree); +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc new file mode 100644 index 00000000..5b5d2831 --- /dev/null +++ b/src/google/protobuf/compiler/importer_unittest.cc @@ -0,0 +1,539 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +namespace { + +#define EXPECT_SUBSTRING(needle, haystack) \ + EXPECT_PRED_FORMAT2(testing::IsSubstring, (needle), (haystack)) + +class MockErrorCollector : public MultiFileErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, int line, int column, + const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", + filename, line, column, message); + } +}; + +// ------------------------------------------------------------------- + +// A dummy implementation of SourceTree backed by a simple map. +class MockSourceTree : public SourceTree { + public: + MockSourceTree() {} + ~MockSourceTree() {} + + void AddFile(const string& name, const char* contents) { + files_[name] = contents; + } + + // implements SourceTree ------------------------------------------- + io::ZeroCopyInputStream* Open(const string& filename) { + const char* contents = FindPtrOrNull(files_, filename); + if (contents == NULL) { + return NULL; + } else { + return new io::ArrayInputStream(contents, strlen(contents)); + } + } + + private: + hash_map files_; +}; + +// =================================================================== + +class ImporterTest : public testing::Test { + protected: + ImporterTest() + : importer_(&source_tree_, &error_collector_) {} + + void AddFile(const string& filename, const char* text) { + source_tree_.AddFile(filename, text); + } + + // Return the collected error text + string error() const { return error_collector_.text_; } + + MockErrorCollector error_collector_; + MockSourceTree source_tree_; + Importer importer_; +}; + +TEST_F(ImporterTest, Import) { + // Test normal importing. + AddFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + const FileDescriptor* file = importer_.Import("foo.proto"); + EXPECT_EQ("", error_collector_.text_); + ASSERT_TRUE(file != NULL); + + ASSERT_EQ(1, file->message_type_count()); + EXPECT_EQ("Foo", file->message_type(0)->name()); + + // Importing again should return same object. + EXPECT_EQ(file, importer_.Import("foo.proto")); +} + +TEST_F(ImporterTest, ImportNested) { + // Test that importing a file which imports another file works. + AddFile("foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "message Foo {\n" + " optional Bar bar = 1;\n" + "}\n"); + AddFile("bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + + // Note that both files are actually parsed by the first call to Import() + // here, since foo.proto imports bar.proto. The second call just returns + // the same ProtoFile for bar.proto which was constructed while importing + // foo.proto. We test that this is the case below by checking that bar + // is among foo's dependencies (by pointer). + const FileDescriptor* foo = importer_.Import("foo.proto"); + const FileDescriptor* bar = importer_.Import("bar.proto"); + EXPECT_EQ("", error_collector_.text_); + ASSERT_TRUE(foo != NULL); + ASSERT_TRUE(bar != NULL); + + // Check that foo's dependency is the same object as bar. + ASSERT_EQ(1, foo->dependency_count()); + EXPECT_EQ(bar, foo->dependency(0)); + + // Check that foo properly cross-links bar. + ASSERT_EQ(1, foo->message_type_count()); + ASSERT_EQ(1, bar->message_type_count()); + ASSERT_EQ(1, foo->message_type(0)->field_count()); + ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE, + foo->message_type(0)->field(0)->type()); + EXPECT_EQ(bar->message_type(0), + foo->message_type(0)->field(0)->message_type()); +} + +TEST_F(ImporterTest, FileNotFound) { + // Error: Parsing a file that doesn't exist. + EXPECT_TRUE(importer_.Import("foo.proto") == NULL); + EXPECT_EQ( + "foo.proto:-1:0: File not found.\n", + error_collector_.text_); +} + +TEST_F(ImporterTest, ImportNotFound) { + // Error: Importing a file that doesn't exist. + AddFile("foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n"); + + EXPECT_TRUE(importer_.Import("foo.proto") == NULL); + EXPECT_EQ( + "bar.proto:-1:0: File not found.\n" + "foo.proto:-1:0: Import \"bar.proto\" was not found or had errors.\n", + error_collector_.text_); +} + +TEST_F(ImporterTest, RecursiveImport) { + // Error: Recursive import. + AddFile("recursive1.proto", + "syntax = \"proto2\";\n" + "import \"recursive2.proto\";\n"); + AddFile("recursive2.proto", + "syntax = \"proto2\";\n" + "import \"recursive1.proto\";\n"); + + EXPECT_TRUE(importer_.Import("recursive1.proto") == NULL); + EXPECT_EQ( + "recursive1.proto:-1:0: File recursively imports itself: recursive1.proto " + "-> recursive2.proto -> recursive1.proto\n" + "recursive2.proto:-1:0: Import \"recursive1.proto\" was not found " + "or had errors.\n" + "recursive1.proto:-1:0: Import \"recursive2.proto\" was not found " + "or had errors.\n", + error_collector_.text_); +} + +// TODO(sanjay): The MapField tests below more properly belong in +// descriptor_unittest, but are more convenient to test here. +TEST_F(ImporterTest, MapFieldValid) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " required string key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + const FileDescriptor* file = importer_.Import("map.proto"); + ASSERT_TRUE(file != NULL) << error_collector_.text_; + EXPECT_EQ("", error_collector_.text_); + + // Check that Map::items points to Item::key + const Descriptor* item_type = file->FindMessageTypeByName("Item"); + ASSERT_TRUE(item_type != NULL); + const Descriptor* map_type = file->FindMessageTypeByName("Map"); + ASSERT_TRUE(map_type != NULL); + const FieldDescriptor* key_field = item_type->FindFieldByName("key"); + ASSERT_TRUE(key_field != NULL); + const FieldDescriptor* items_field = map_type->FindFieldByName("items"); + ASSERT_TRUE(items_field != NULL); + EXPECT_EQ(items_field->experimental_map_key(), key_field); +} + +TEST_F(ImporterTest, MapFieldNotRepeated) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " required string key = 1;\n" + "}\n" + "message Map {\n" + " required Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("only allowed for repeated fields", error()); +} + +TEST_F(ImporterTest, MapFieldNotMessageType) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Map {\n" + " repeated int32 items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("only allowed for fields with a message type", error()); +} + +TEST_F(ImporterTest, MapFieldTypeNotFound) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Map {\n" + " repeated Unknown items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("not defined", error()); +} + +TEST_F(ImporterTest, MapFieldKeyNotFound) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " required string key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"badkey\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("Could not find field", error()); +} + +TEST_F(ImporterTest, MapFieldKeyRepeated) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " repeated string key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("must not name a repeated field", error()); +} + +TEST_F(ImporterTest, MapFieldKeyNotScalar) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message ItemKey { }\n" + "message Item {\n" + " required ItemKey key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("must name a scalar or string", error()); +} + +// =================================================================== + +class DiskSourceTreeTest : public testing::Test { + protected: + virtual void SetUp() { + dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_1"); + dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_2"); + + for (int i = 0; i < dirnames_.size(); i++) { + if (File::Exists(dirnames_[i])) { + File::DeleteRecursively(dirnames_[i], NULL, NULL); + } + GOOGLE_CHECK(File::CreateDir(dirnames_[i].c_str(), DEFAULT_FILE_MODE)); + } + } + + virtual void TearDown() { + for (int i = 0; i < dirnames_.size(); i++) { + File::DeleteRecursively(dirnames_[i], NULL, NULL); + } + } + + void AddFile(const string& filename, const char* contents) { + File::WriteStringToFileOrDie(contents, filename); + } + + void AddSubdir(const string& dirname) { + GOOGLE_CHECK(File::CreateDir(dirname.c_str(), DEFAULT_FILE_MODE)); + } + + void ExpectFileContents(const string& filename, + const char* expected_contents) { + scoped_ptr input(source_tree_.Open(filename)); + + ASSERT_FALSE(input == NULL); + + // Read all the data from the file. + string file_contents; + const void* data; + int size; + while (input->Next(&data, &size)) { + file_contents.append(reinterpret_cast(data), size); + } + + EXPECT_EQ(expected_contents, file_contents); + } + + void ExpectFileNotFound(const string& filename) { + scoped_ptr input(source_tree_.Open(filename)); + EXPECT_TRUE(input == NULL); + } + + DiskSourceTree source_tree_; + + // Paths of two on-disk directories to use during the test. + vector dirnames_; +}; + +TEST_F(DiskSourceTreeTest, MapRoot) { + // Test opening a file in a directory that is mapped to the root of the + // source tree. + AddFile(dirnames_[0] + "/foo", "Hello World!"); + source_tree_.MapPath("", dirnames_[0]); + + ExpectFileContents("foo", "Hello World!"); + ExpectFileNotFound("bar"); +} + +TEST_F(DiskSourceTreeTest, MapDirectory) { + // Test opening a file in a directory that is mapped to somewhere other + // than the root of the source tree. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + source_tree_.MapPath("baz", dirnames_[0]); + + ExpectFileContents("baz/foo", "Hello World!"); + ExpectFileNotFound("baz/bar"); + ExpectFileNotFound("foo"); + ExpectFileNotFound("bar"); + + // Non-canonical file names should not work. + ExpectFileNotFound("baz//foo"); + ExpectFileNotFound("baz/../baz/foo"); + ExpectFileNotFound("baz/./foo"); + ExpectFileNotFound("baz/foo/"); +} + +TEST_F(DiskSourceTreeTest, NoParent) { + // Test that we cannot open files in a parent of a mapped directory. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + AddSubdir(dirnames_[0] + "/bar"); + AddFile(dirnames_[0] + "/bar/baz", "Blah."); + source_tree_.MapPath("", dirnames_[0] + "/bar"); + + ExpectFileContents("baz", "Blah."); + ExpectFileNotFound("../foo"); + ExpectFileNotFound("../bar/baz"); +} + +TEST_F(DiskSourceTreeTest, MapFile) { + // Test opening a file that is mapped directly into the source tree. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + source_tree_.MapPath("foo", dirnames_[0] + "/foo"); + + ExpectFileContents("foo", "Hello World!"); + ExpectFileNotFound("bar"); +} + +TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) { + // Test mapping and searching multiple directories. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + AddFile(dirnames_[1] + "/foo", "This file should be hidden."); + AddFile(dirnames_[1] + "/bar", "Goodbye World!"); + source_tree_.MapPath("", dirnames_[0]); + source_tree_.MapPath("", dirnames_[1]); + + ExpectFileContents("foo", "Hello World!"); + ExpectFileContents("bar", "Goodbye World!"); + ExpectFileNotFound("baz"); +} + +TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) { + // Test that directories are always searched in order, even when a latter + // directory is more-specific than a former one. + + // Create the "bar" directory so we can put a file in it. + ASSERT_TRUE(File::CreateDir((dirnames_[0] + "/bar").c_str(), + DEFAULT_FILE_MODE)); + + // Add files and map paths. + AddFile(dirnames_[0] + "/bar/foo", "Hello World!"); + AddFile(dirnames_[1] + "/foo", "This file should be hidden."); + source_tree_.MapPath("", dirnames_[0]); + source_tree_.MapPath("bar", dirnames_[1]); + + // Check. + ExpectFileContents("bar/foo", "Hello World!"); +} + +TEST_F(DiskSourceTreeTest, DiskFileToVirtualFile) { + // Test DiskFileToVirtualFile. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + AddFile(dirnames_[1] + "/foo", "This file should be hidden."); + source_tree_.MapPath("bar", dirnames_[0]); + source_tree_.MapPath("bar", dirnames_[1]); + + string virtual_file; + string shadowing_disk_file; + + EXPECT_EQ(DiskSourceTree::NO_MAPPING, + source_tree_.DiskFileToVirtualFile( + "/foo", &virtual_file, &shadowing_disk_file)); + + EXPECT_EQ(DiskSourceTree::SHADOWED, + source_tree_.DiskFileToVirtualFile( + dirnames_[1] + "/foo", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("bar/foo", virtual_file); + EXPECT_EQ(dirnames_[0] + "/foo", shadowing_disk_file); + + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + dirnames_[1] + "/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("bar/baz", virtual_file); + + EXPECT_EQ(DiskSourceTree::SUCCESS, + source_tree_.DiskFileToVirtualFile( + dirnames_[0] + "/foo", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("bar/foo", virtual_file); +} + +TEST_F(DiskSourceTreeTest, DiskFileToVirtualFileCanonicalization) { + // Test handling of "..", ".", etc. in DiskFileToVirtualFile(). + + source_tree_.MapPath("dir1", ".."); + source_tree_.MapPath("dir2", "../../foo"); + source_tree_.MapPath("dir3", "./foo/bar/."); + source_tree_.MapPath("dir4", "."); + source_tree_.MapPath("", "/qux"); + + string virtual_file; + string shadowing_disk_file; + + // "../.." should not be considered to be under "..". + EXPECT_EQ(DiskSourceTree::NO_MAPPING, + source_tree_.DiskFileToVirtualFile( + "../../baz", &virtual_file, &shadowing_disk_file)); + + // But "../baz" should be. + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "../baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir1/baz", virtual_file); + + // "../../foo/baz" is under "../../foo". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "../../foo/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir2/baz", virtual_file); + + // "foo/./bar/baz" is under "./foo/bar/.". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "foo/bar/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir3/baz", virtual_file); + + // "bar" is under ".". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "bar", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir4/bar", virtual_file); + + // "/qux/baz" is under "/qux". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "/qux/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("baz", virtual_file); +} + +} // namespace + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc new file mode 100644 index 00000000..aa15a468 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -0,0 +1,189 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor) + : descriptor_(descriptor) { + for (int i = 0; i < descriptor_->value_count(); i++) { + const EnumValueDescriptor* value = descriptor_->value(i); + const EnumValueDescriptor* canonical_value = + descriptor_->FindValueByNumber(value->number()); + + if (value == canonical_value) { + canonical_values_.push_back(value); + } else { + Alias alias; + alias.value = value; + alias.canonical_value = canonical_value; + aliases_.push_back(alias); + } + } +} + +EnumGenerator::~EnumGenerator() {} + +void EnumGenerator::Generate(io::Printer* printer) { + bool is_own_file = + descriptor_->containing_type() == NULL && + descriptor_->file()->options().java_multiple_files(); + printer->Print( + "public $static$ enum $classname$ {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + printer->Indent(); + + for (int i = 0; i < canonical_values_.size(); i++) { + map vars; + vars["name"] = canonical_values_[i]->name(); + vars["index"] = SimpleItoa(canonical_values_[i]->index()); + vars["number"] = SimpleItoa(canonical_values_[i]->number()); + printer->Print(vars, + "$name$($index$, $number$),\n"); + } + + printer->Print( + ";\n" + "\n"); + + // ----------------------------------------------------------------- + + for (int i = 0; i < aliases_.size(); i++) { + map vars; + vars["classname"] = descriptor_->name(); + vars["name"] = aliases_[i].value->name(); + vars["canonical_name"] = aliases_[i].canonical_value->name(); + printer->Print(vars, + "public static final $classname$ $name$ = $canonical_name$;\n"); + } + + // ----------------------------------------------------------------- + + printer->Print( + "\n" + "public final int getNumber() { return value; }\n" + "\n" + "public static $classname$ valueOf(int value) {\n" + " switch (value) {\n", + "classname", descriptor_->name()); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < canonical_values_.size(); i++) { + printer->Print( + "case $number$: return $name$;\n", + "name", canonical_values_[i]->name(), + "number", SimpleItoa(canonical_values_[i]->number())); + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " default: return null;\n" + " }\n" + "}\n" + "\n"); + + // ----------------------------------------------------------------- + // Reflection + + printer->Print( + "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n" + " getValueDescriptor() {\n" + " return getDescriptor().getValues().get(index);\n" + "}\n" + "public final com.google.protobuf.Descriptors.EnumDescriptor\n" + " getDescriptorForType() {\n" + " return getDescriptor();\n" + "}\n" + "public static final com.google.protobuf.Descriptors.EnumDescriptor\n" + " getDescriptor() {\n"); + + // TODO(kenton): Cache statically? Note that we can't access descriptors + // at module init time because it wouldn't work with descriptor.proto, but + // we can cache the value the first time getDescriptor() is called. + if (descriptor_->containing_type() == NULL) { + printer->Print( + " return $file$.getDescriptor().getEnumTypes().get($index$);\n", + "file", ClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + } else { + printer->Print( + " return $parent$.getDescriptor().getEnumTypes().get($index$);\n", + "parent", ClassName(descriptor_->containing_type()), + "index", SimpleItoa(descriptor_->index())); + } + + printer->Print( + "}\n" + "\n" + "private static final $classname$[] VALUES = {\n" + " ", + "classname", descriptor_->name()); + + for (int i = 0; i < descriptor_->value_count(); i++) { + printer->Print("$name$, ", + "name", descriptor_->value(i)->name()); + } + + printer->Print( + "\n" + "};\n" + "public static $classname$ valueOf(\n" + " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n" + " if (desc.getType() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"EnumValueDescriptor is not for this type.\");\n" + " }\n" + " return VALUES[desc.getIndex()];\n" + "}\n", + "classname", descriptor_->name()); + + // ----------------------------------------------------------------- + + printer->Print( + "private final int index;\n" + "private final int value;\n" + "private $classname$(int index, int value) {\n" + " this.index = index;\n" + " this.value = value;\n" + "}\n", + "classname", descriptor_->name()); + + printer->Outdent(); + printer->Print("}\n\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum.h b/src/google/protobuf/compiler/java/java_enum.h new file mode 100644 index 00000000..f0a16036 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum.h @@ -0,0 +1,70 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class EnumGenerator { + public: + explicit EnumGenerator(const EnumDescriptor* descriptor); + ~EnumGenerator(); + + void Generate(io::Printer* printer); + + private: + const EnumDescriptor* descriptor_; + + // The proto language allows multiple enum constants to have the same numeric + // value. Java, however, does not allow multiple enum constants to be + // considered equivalent. We treat the first defined constant for any + // given numeric value as "canonical" and the rest as aliases of that + // canonical value. + vector canonical_values_; + + struct Alias { + const EnumValueDescriptor* value; + const EnumValueDescriptor* canonical_value; + }; + vector aliases_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_H__ diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc new file mode 100644 index 00000000..cb80c4b8 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -0,0 +1,264 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetEnumVariables(const FieldDescriptor* descriptor, + map* variables) { + const EnumValueDescriptor* default_value; + default_value = descriptor->default_value_enum(); + + string type = ClassName(descriptor->enum_type()); + + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = type; + (*variables)["default"] = type + "." + default_value->name(); +} + +} // namespace + +// =================================================================== + +EnumFieldGenerator:: +EnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +EnumFieldGenerator::~EnumFieldGenerator() {} + +void EnumFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\n" + "private $type$ $name$_ = $default$;\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" + "public $type$ get$capitalized_name$() { return $name$_; }\n"); +} + +void EnumFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\n" + " return result.has$capitalized_name$();\n" + "}\n" + "public $type$ get$capitalized_name$() {\n" + " return result.get$capitalized_name$();\n" + "}\n" + "public Builder set$capitalized_name$($type$ value) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = value;\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.has$capitalized_name$ = false;\n" + " result.$name$_ = $default$;\n" + " return this;\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do here for enum types. +} + +void EnumFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n" + " unknownFields.mergeVarintField($number$, rawValue);\n" + "} else {\n" + " set$capitalized_name$(value);\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " output.writeEnum($number$, get$capitalized_name$().getNumber());\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, get$capitalized_name$().getNumber());\n" + "}\n"); +} + +string EnumFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->enum_type()); +} + +// =================================================================== + +RepeatedEnumFieldGenerator:: +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} + +void RepeatedEnumFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n" + "public int get$capitalized_name$Count() {\n" + " return result.get$capitalized_name$Count();\n" + "}\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return result.get$capitalized_name$(index);\n" + "}\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " result.$name$_.set(index, value);\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$ value) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.add(value);\n" + " return this;\n" + "}\n" + "public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " super.addAll(values, result.$name$_);\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.$name$_ = java.util.Collections.emptyList();\n" + " return this;\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.addAll(other.$name$_);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" + " result.$name$_ =\n" + " java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n" + " unknownFields.mergeVarintField($number$, rawValue);\n" + "} else {\n" + " add$capitalized_name$(value);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.writeEnum($number$, element.getNumber());\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, element.getNumber());\n" + "}\n"); +} + +string RepeatedEnumFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->enum_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h new file mode 100644 index 00000000..473ba617 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class EnumFieldGenerator : public FieldGenerator { + public: + explicit EnumFieldGenerator(const FieldDescriptor* descriptor); + ~EnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); +}; + +class RepeatedEnumFieldGenerator : public FieldGenerator { + public: + explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedEnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc new file mode 100644 index 00000000..1b637fab --- /dev/null +++ b/src/google/protobuf/compiler/java/java_extension.cc @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) {} + +ExtensionGenerator::~ExtensionGenerator() {} + +void ExtensionGenerator::Generate(io::Printer* printer) { + map vars; + vars["name"] = UnderscoresToCamelCase(descriptor_); + vars["containing_type"] = ClassName(descriptor_->containing_type()); + vars["index"] = SimpleItoa(descriptor_->index()); + + JavaType java_type = GetJavaType(descriptor_); + string singular_type; + switch (java_type) { + case JAVATYPE_MESSAGE: + vars["type"] = ClassName(descriptor_->message_type()); + break; + case JAVATYPE_ENUM: + vars["type"] = ClassName(descriptor_->enum_type()); + break; + default: + vars["type"] = BoxedPrimitiveTypeName(java_type); + break; + } + + if (descriptor_->is_repeated()) { + printer->Print(vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " java.util.List<$type$>> $name$ =\n" + " com.google.protobuf.GeneratedMessage\n" + " .newRepeatedGeneratedExtension(\n" + " getDescriptor().getExtensions().get($index$),\n" + " $type$.class);\n"); + } else { + printer->Print(vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ =\n" + " com.google.protobuf.GeneratedMessage.newGeneratedExtension(\n" + " getDescriptor().getExtensions().get($index$),\n" + " $type$.class);\n"); + } +} + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_extension.h b/src/google/protobuf/compiler/java/java_extension.h new file mode 100644 index 00000000..9088fecd --- /dev/null +++ b/src/google/protobuf/compiler/java/java_extension.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__ + +#include + +namespace google { +namespace protobuf { + class FieldDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +// Generates code for an extension, which may be within the scope of some +// message or may be at file scope. This is much simpler than FieldGenerator +// since extensions are just simple identifiers with interesting types. +class ExtensionGenerator { + public: + explicit ExtensionGenerator(const FieldDescriptor* descriptor); + ~ExtensionGenerator(); + + void Generate(io::Printer* printer); + + private: + const FieldDescriptor* descriptor_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc new file mode 100644 index 00000000..b7197cac --- /dev/null +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -0,0 +1,88 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +FieldGenerator::~FieldGenerator() {} + +FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_( + new scoped_ptr[descriptor->field_count()]), + extension_generators_( + new scoped_ptr[descriptor->extension_count()]) { + + // Construct all the FieldGenerators. + for (int i = 0; i < descriptor->field_count(); i++) { + field_generators_[i].reset(MakeGenerator(descriptor->field(i))); + } + for (int i = 0; i < descriptor->extension_count(); i++) { + extension_generators_[i].reset(MakeGenerator(descriptor->extension(i))); + } +} + +FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) { + if (field->is_repeated()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + return new RepeatedMessageFieldGenerator(field); + case JAVATYPE_ENUM: + return new RepeatedEnumFieldGenerator(field); + default: + return new RepeatedPrimitiveFieldGenerator(field); + } + } else { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + return new MessageFieldGenerator(field); + case JAVATYPE_ENUM: + return new EnumFieldGenerator(field); + default: + return new PrimitiveFieldGenerator(field); + } + } +} + +FieldGeneratorMap::~FieldGeneratorMap() {} + +const FieldGenerator& FieldGeneratorMap::get( + const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +const FieldGenerator& FieldGeneratorMap::get_extension(int index) const { + return *extension_generators_[index]; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h new file mode 100644 index 00000000..ef47c735 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_field.h @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class FieldGenerator { + public: + FieldGenerator() {} + virtual ~FieldGenerator(); + + virtual void GenerateMembers(io::Printer* printer) const = 0; + virtual void GenerateBuilderMembers(io::Printer* printer) const = 0; + virtual void GenerateMergingCode(io::Printer* printer) const = 0; + virtual void GenerateBuildingCode(io::Printer* printer) const = 0; + virtual void GenerateParsingCode(io::Printer* printer) const = 0; + virtual void GenerateSerializationCode(io::Printer* printer) const = 0; + virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; + + virtual string GetBoxedType() const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); +}; + +// Convenience class which constructs FieldGenerators for a Descriptor. +class FieldGeneratorMap { + public: + explicit FieldGeneratorMap(const Descriptor* descriptor); + ~FieldGeneratorMap(); + + const FieldGenerator& get(const FieldDescriptor* field) const; + const FieldGenerator& get_extension(int index) const; + + private: + const Descriptor* descriptor_; + scoped_array > field_generators_; + scoped_array > extension_generators_; + + static FieldGenerator* MakeGenerator(const FieldDescriptor* field); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc new file mode 100644 index 00000000..e7e8618d --- /dev/null +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -0,0 +1,249 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +FileGenerator::FileGenerator(const FileDescriptor* file) + : file_(file), + java_package_(FileJavaPackage(file)), + classname_(FileClassName(file)) {} + +FileGenerator::~FileGenerator() {} + +bool FileGenerator::Validate(string* error) { + // Check that no class name matches the file's class name. This is a common + // problem that leads to Java compile errors that can be hard to understand. + // It's especially bad when using the java_multiple_files, since we would + // end up overwriting the outer class with one of the inner ones. + + bool found_conflict = false; + for (int i = 0; i < file_->enum_type_count() && !found_conflict; i++) { + if (file_->enum_type(i)->name() == classname_) { + found_conflict = true; + } + } + for (int i = 0; i < file_->message_type_count() && !found_conflict; i++) { + if (file_->message_type(i)->name() == classname_) { + found_conflict = true; + } + } + for (int i = 0; i < file_->service_count() && !found_conflict; i++) { + if (file_->service(i)->name() == classname_) { + found_conflict = true; + } + } + + if (found_conflict) { + error->assign(file_->name()); + error->append( + ": Cannot generate Java output because the file's outer class name, \""); + error->append(classname_); + error->append( + "\", matches the name of one of the types declared inside it. " + "Please either rename the type or use the java_outer_classname " + "option to specify a different outer class name for the .proto file."); + return false; + } + + return true; +} + +void FileGenerator::Generate(io::Printer* printer) { + // We don't import anything because we refer to all classes by their + // fully-qualified names in the generated source. + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n"); + if (!java_package_.empty()) { + printer->Print( + "package $package$;\n" + "\n", + "package", java_package_); + } + printer->Print( + "public final class $classname$ {\n" + " private $classname$() {}\n", + "classname", classname_); + printer->Indent(); + + // ----------------------------------------------------------------- + + // Embed the descriptor. We simply serialize the entire FileDescriptorProto + // and embed it as a string literal, which is parsed and built into real + // descriptors at initialization time. We unfortunately have to put it in + // a string literal, not a byte array, because apparently using a literal + // byte array causes the Java compiler to generate *instructions* to + // initialize each and every byte of the array, e.g. as if you typed: + // b[0] = 123; b[1] = 456; b[2] = 789; + // This makes huge bytecode files and can easily hit the compiler's internal + // code size limits (error "code to large"). String literals are apparently + // embedded raw, which is what we want. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + + printer->Print( + "public static com.google.protobuf.Descriptors.FileDescriptor\n" + " getDescriptor() {\n" + " return descriptor;\n" + "}\n" + "private static final com.google.protobuf.Descriptors.FileDescriptor\n" + " descriptor = buildDescriptor();\n" + "private static\n" + " com.google.protobuf.Descriptors.FileDescriptor\n" + " buildDescriptor() {\n" + " java.lang.String descriptorData =\n"); + printer->Indent(); + printer->Indent(); + + // Only write 40 bytes per line. + static const int kBytesPerLine = 40; + for (int i = 0; i < file_data.size(); i += kBytesPerLine) { + if (i > 0) printer->Print(" +\n"); + printer->Print("\"$data$\"", + "data", CEscape(file_data.substr(i, kBytesPerLine))); + } + printer->Print(";\n"); + + printer->Outdent(); + printer->Print( + "try {\n" + " return com.google.protobuf.Descriptors.FileDescriptor\n" + " .internalBuildGeneratedFileFrom(descriptorData,\n" + " new com.google.protobuf.Descriptors.FileDescriptor[] {\n"); + + for (int i = 0; i < file_->dependency_count(); i++) { + printer->Print( + " $dependency$.getDescriptor(),\n", + "dependency", ClassName(file_->dependency(i))); + } + + printer->Print( + " });\n" + "} catch (Exception e) {\n" + " throw new RuntimeException(\n" + " \"Failed to parse protocol buffer descriptor for \" +\n" + " \"\\\"$filename$\\\".\", e);\n" + "}\n", + "filename", file_->name()); + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + // ----------------------------------------------------------------- + + if (!file_->options().java_multiple_files()) { + for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator(file_->enum_type(i)).Generate(printer); + } + for (int i = 0; i < file_->message_type_count(); i++) { + MessageGenerator(file_->message_type(i)).Generate(printer); + } + for (int i = 0; i < file_->service_count(); i++) { + ServiceGenerator(file_->service(i)).Generate(printer); + } + } + + // Extensions must be generated in the outer class since they are values, + // not classes. + for (int i = 0; i < file_->extension_count(); i++) { + ExtensionGenerator(file_->extension(i)).Generate(printer); + } + + // Static variables. + for (int i = 0; i < file_->message_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + MessageGenerator(file_->message_type(i)).GenerateStaticVariables(printer); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +template +static void GenerateSibling(const string& package_dir, + const string& java_package, + const DescriptorClass* descriptor, + OutputDirectory* output_directory, + vector* file_list) { + string filename = package_dir + descriptor->name() + ".java"; + file_list->push_back(filename); + + scoped_ptr output( + output_directory->Open(filename)); + io::Printer printer(output.get(), '$'); + + printer.Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n"); + if (!java_package.empty()) { + printer.Print( + "package $package$;\n" + "\n", + "package", java_package); + } + + GeneratorClass(descriptor).Generate(&printer); +} + +void FileGenerator::GenerateSiblings(const string& package_dir, + OutputDirectory* output_directory, + vector* file_list) { + if (file_->options().java_multiple_files()) { + for (int i = 0; i < file_->enum_type_count(); i++) { + GenerateSibling(package_dir, java_package_, + file_->enum_type(i), + output_directory, file_list); + } + for (int i = 0; i < file_->message_type_count(); i++) { + GenerateSibling(package_dir, java_package_, + file_->message_type(i), + output_directory, file_list); + } + for (int i = 0; i < file_->service_count(); i++) { + GenerateSibling(package_dir, java_package_, + file_->service(i), + output_directory, file_list); + } + } +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h new file mode 100644 index 00000000..9eeec253 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_file.h @@ -0,0 +1,78 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + class FileDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } + namespace compiler { + class OutputDirectory; // code_generator.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class FileGenerator { + public: + explicit FileGenerator(const FileDescriptor* file); + ~FileGenerator(); + + // Checks for problems that would otherwise lead to cryptic compile errors. + // Returns true if there are no problems, or writes an error description to + // the given string and returns false otherwise. + bool Validate(string* error); + + void Generate(io::Printer* printer); + + // If we aren't putting everything into one file, this will write all the + // files other than the outer file (i.e. one for each message, enum, and + // service type). + void GenerateSiblings(const string& package_dir, + OutputDirectory* output_directory, + vector* file_list); + + const string& java_package() { return java_package_; } + const string& classname() { return classname_; } + + private: + const FileDescriptor* file_; + string java_package_; + string classname_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ diff --git a/src/google/protobuf/compiler/java/java_generator.cc b/src/google/protobuf/compiler/java/java_generator.cc new file mode 100644 index 00000000..89a32da4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_generator.cc @@ -0,0 +1,133 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +// Parses a set of comma-delimited name/value pairs, e.g.: +// "foo=bar,baz,qux=corge" +// parses to the pairs: +// ("foo", "bar"), ("baz", ""), ("qux", "corge") +void ParseOptions(const string& text, vector >* output) { + vector parts; + SplitStringUsing(text, ",", &parts); + + for (int i = 0; i < parts.size(); i++) { + string::size_type equals_pos = parts[i].find_first_of('='); + pair value; + if (equals_pos == string::npos) { + value.first = parts[i]; + value.second = ""; + } else { + value.first = parts[i].substr(0, equals_pos); + value.second = parts[i].substr(equals_pos + 1); + } + output->push_back(value); + } +} + +} // namespace + +JavaGenerator::JavaGenerator() {} +JavaGenerator::~JavaGenerator() {} + +bool JavaGenerator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + vector > options; + ParseOptions(parameter, &options); + + // ----------------------------------------------------------------- + // parse generator options + + // Name a file where we will write a list of generated file names, one + // per line. + string output_list_file; + + for (int i = 0; i < options.size(); i++) { + if (options[i].first == "output_list_file") { + output_list_file = options[i].second; + } else { + *error = "Unknown generator option: " + options[i].first; + return false; + } + } + + + // ----------------------------------------------------------------- + + + FileGenerator file_generator(file); + if (!file_generator.Validate(error)) { + return false; + } + + string package_dir = + StringReplace(file_generator.java_package(), ".", "/", true); + if (!package_dir.empty()) package_dir += "/"; + + vector all_files; + + string java_filename = package_dir; + java_filename += file_generator.classname(); + java_filename += ".java"; + all_files.push_back(java_filename); + + // Generate main java file. + scoped_ptr output( + output_directory->Open(java_filename)); + io::Printer printer(output.get(), '$'); + file_generator.Generate(&printer); + + // Generate sibling files. + file_generator.GenerateSiblings(package_dir, output_directory, &all_files); + + // Generate output list if requested. + if (!output_list_file.empty()) { + // Generate output list. This is just a simple text file placed in a + // deterministic location which lists the .java files being generated. + scoped_ptr srclist_raw_output( + output_directory->Open(output_list_file)); + io::Printer srclist_printer(srclist_raw_output.get(), '$'); + for (int i = 0; i < all_files.size(); i++) { + srclist_printer.Print("$filename$\n", "filename", all_files[i]); + } + } + + return true; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_generator.h b/src/google/protobuf/compiler/java/java_generator.h new file mode 100644 index 00000000..11886aa0 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_generator.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Generates Java code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +// CodeGenerator implementation which generates Java code. If you create your +// own protocol compiler binary and you want it to support Java output, you +// can do so by registering an instance of this CodeGenerator with the +// CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT JavaGenerator : public CodeGenerator { + public: + JavaGenerator(); + ~JavaGenerator(); + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JavaGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc new file mode 100644 index 00000000..4ba82c2a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -0,0 +1,229 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +const char kThickSeparator[] = + "// ===================================================================\n"; +const char kThinSeparator[] = + "// -------------------------------------------------------------------\n"; + +namespace { + +const char* kDefaultPackage = ""; + +const string& FieldName(const FieldDescriptor* field) { + // Groups are hacky: The name of the field is just the lower-cased name + // of the group type. In Java, though, we would like to retain the original + // capitalization of the type name. + if (field->type() == FieldDescriptor::TYPE_GROUP) { + return field->message_type()->name(); + } else { + return field->name(); + } +} + +string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) { + string result; + // Note: I distrust ctype.h due to locales. + for (int i = 0; i < input.size(); i++) { + if ('a' <= input[i] && input[i] <= 'z') { + if (cap_next_letter) { + result += input[i] + ('A' - 'a'); + } else { + result += input[i]; + } + cap_next_letter = false; + } else if ('A' <= input[i] && input[i] <= 'Z') { + if (i == 0 && !cap_next_letter) { + // Force first letter to lower-case unless explicitly told to + // capitalize it. + result += input[i] + ('a' - 'A'); + } else { + // Capital letters after the first are left as-is. + result += input[i]; + } + cap_next_letter = false; + } else if ('0' <= input[i] && input[i] <= '9') { + result += input[i]; + cap_next_letter = true; + } else { + cap_next_letter = true; + } + } + return result; +} + +} // namespace + +string UnderscoresToCamelCase(const FieldDescriptor* field) { + return UnderscoresToCamelCaseImpl(FieldName(field), false); +} + +string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { + return UnderscoresToCamelCaseImpl(FieldName(field), true); +} + +string UnderscoresToCamelCase(const MethodDescriptor* method) { + return UnderscoresToCamelCaseImpl(method->name(), false); +} + +string StripProto(const string& filename) { + if (HasSuffixString(filename, ".protodevel")) { + return StripSuffixString(filename, ".protodevel"); + } else { + return StripSuffixString(filename, ".proto"); + } +} + +string FileClassName(const FileDescriptor* file) { + if (file->options().has_java_outer_classname()) { + return file->options().java_outer_classname(); + } else { + string basename; + string::size_type last_slash = file->name().find_last_of('/'); + if (last_slash == string::npos) { + basename = file->name(); + } else { + basename = file->name().substr(last_slash + 1); + } + return UnderscoresToCamelCaseImpl(StripProto(basename), true); + } +} + +string FileJavaPackage(const FileDescriptor* file) { + if (file->options().has_java_package()) { + return file->options().java_package(); + } else { + string result = kDefaultPackage; + if (!file->package().empty()) { + if (!result.empty()) result += '.'; + result += file->package(); + } + return result; + } +} + +string ToJavaName(const string& full_name, const FileDescriptor* file) { + string result; + if (file->options().java_multiple_files()) { + result = FileJavaPackage(file); + } else { + result = ClassName(file); + } + if (!result.empty()) { + result += '.'; + } + if (file->package().empty()) { + result += full_name; + } else { + // Strip the proto package from full_name since we've replaced it with + // the Java package. + result += full_name.substr(file->package().size() + 1); + } + return result; +} + +string ClassName(const FileDescriptor* descriptor) { + string result = FileJavaPackage(descriptor); + if (!result.empty()) result += '.'; + result += FileClassName(descriptor); + return result; +} + +JavaType GetJavaType(FieldDescriptor::Type field_type) { + switch (field_type) { + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_SFIXED32: + return JAVATYPE_INT; + + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_UINT64: + case FieldDescriptor::TYPE_SINT64: + case FieldDescriptor::TYPE_FIXED64: + case FieldDescriptor::TYPE_SFIXED64: + return JAVATYPE_LONG; + + case FieldDescriptor::TYPE_FLOAT: + return JAVATYPE_FLOAT; + + case FieldDescriptor::TYPE_DOUBLE: + return JAVATYPE_DOUBLE; + + case FieldDescriptor::TYPE_BOOL: + return JAVATYPE_BOOLEAN; + + case FieldDescriptor::TYPE_STRING: + return JAVATYPE_STRING; + + case FieldDescriptor::TYPE_BYTES: + return JAVATYPE_BYTES; + + case FieldDescriptor::TYPE_ENUM: + return JAVATYPE_ENUM; + + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_MESSAGE: + return JAVATYPE_MESSAGE; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return JAVATYPE_INT; +} + +const char* BoxedPrimitiveTypeName(JavaType type) { + switch (type) { + case JAVATYPE_INT : return "java.lang.Integer"; + case JAVATYPE_LONG : return "java.lang.Long"; + case JAVATYPE_FLOAT : return "java.lang.Float"; + case JAVATYPE_DOUBLE : return "java.lang.Double"; + case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; + case JAVATYPE_STRING : return "java.lang.String"; + case JAVATYPE_BYTES : return "com.google.protobuf.ByteString"; + case JAVATYPE_ENUM : return NULL; + case JAVATYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h new file mode 100644 index 00000000..74376330 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -0,0 +1,102 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_HELPERS_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_HELPERS_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +// Commonly-used separator comments. Thick is a line of '=', thin is a line +// of '-'. +extern const char kThickSeparator[]; +extern const char kThinSeparator[]; + +// Converts the field's name to camel-case, e.g. "foo_bar_baz" becomes +// "fooBarBaz" or "FooBarBaz", respectively. +string UnderscoresToCamelCase(const FieldDescriptor* field); +string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field); + +// Similar, but for method names. (Typically, this merely has the effect +// of lower-casing the first letter of the name.) +string UnderscoresToCamelCase(const MethodDescriptor* method); + +// Strips ".proto" or ".protodevel" from the end of a filename. +string StripProto(const string& filename); + +// Gets the unqualified class name for the file. Each .proto file becomes a +// single Java class, with all its contents nested in that class. +string FileClassName(const FileDescriptor* file); + +// Returns the file's Java package name. +string FileJavaPackage(const FileDescriptor* file); + +// Converts the given fully-qualified name in the proto namespace to its +// fully-qualified name in the Java namespace, given that it is in the given +// file. +string ToJavaName(const string& full_name, const FileDescriptor* file); + +// These return the fully-qualified class name corresponding to the given +// descriptor. +inline string ClassName(const Descriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +inline string ClassName(const EnumDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +inline string ClassName(const ServiceDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +string ClassName(const FileDescriptor* descriptor); + +enum JavaType { + JAVATYPE_INT, + JAVATYPE_LONG, + JAVATYPE_FLOAT, + JAVATYPE_DOUBLE, + JAVATYPE_BOOLEAN, + JAVATYPE_STRING, + JAVATYPE_BYTES, + JAVATYPE_ENUM, + JAVATYPE_MESSAGE +}; + +JavaType GetJavaType(FieldDescriptor::Type field_type); + +inline JavaType GetJavaType(const FieldDescriptor* field) { + return GetJavaType(field->type()); +} + +// Get the fully-qualified class name for a boxed primitive type, e.g. +// "java.lang.Integer" for JAVATYPE_INT. Returns NULL for enum and message +// types. +const char* BoxedPrimitiveTypeName(JavaType type); + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_HELPERS_H__ diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc new file mode 100644 index 00000000..ebba1f7b --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -0,0 +1,722 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; + +namespace { + +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { + // Print the field's proto-syntax definition as a comment. We don't want to + // print group bodies so we cut off after the first line. + string def = field->DebugString(); + printer->Print("// $def$\n", + "def", def.substr(0, def.find_first_of('\n'))); +} + +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +struct ExtensionRangeOrdering { + bool operator()(const Descriptor::ExtensionRange* a, + const Descriptor::ExtensionRange* b) const { + return a->start < b->start; + } +}; + +// Sort the fields of the given Descriptor by number into a new[]'d array +// and return it. +const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { + const FieldDescriptor** fields = + new const FieldDescriptor*[descriptor->field_count()]; + for (int i = 0; i < descriptor->field_count(); i++) { + fields[i] = descriptor->field(i); + } + sort(fields, fields + descriptor->field_count(), + FieldOrderingByNumber()); + return fields; +} + +// Get an identifier that uniquely identifies this type within the file. +// This is used to declare static variables related to this type at the +// outermost file scope. +string UniqueFileScopeIdentifier(const Descriptor* descriptor) { + return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); +} + +// Returns true if the message type has any required fields. If it doesn't, +// we can optimize out calls to its isInitialized() method. +// +// already_seen is used to avoid checking the same type multiple times +// (and also to protect against recursion). +static bool HasRequiredFields( + const Descriptor* type, + hash_set* already_seen) { + if (already_seen->count(type) > 0) { + // The type is already in cache. This means that either: + // a. The type has no required fields. + // b. We are in the midst of checking if the type has required fields, + // somewhere up the stack. In this case, we know that if the type + // has any required fields, they'll be found when we return to it, + // and the whole call to HasRequiredFields() will return true. + // Therefore, we don't have to check if this type has required fields + // here. + return false; + } + already_seen->insert(type); + + // If the type has extensions, an extension with message type could contain + // required fields, so we have to be conservative and assume such an + // extension exists. + if (type->extension_range_count() > 0) return true; + + for (int i = 0; i < type->field_count(); i++) { + const FieldDescriptor* field = type->field(i); + if (field->is_required()) { + return true; + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (HasRequiredFields(field->message_type(), already_seen)) { + return true; + } + } + } + + return false; +} + +static bool HasRequiredFields(const Descriptor* type) { + hash_set already_seen; + return HasRequiredFields(type, &already_seen); +} + +} // namespace + +// =================================================================== + +MessageGenerator::MessageGenerator(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_(descriptor) { +} + +MessageGenerator::~MessageGenerator() {} + +void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { + // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is + // used in the construction of descriptors, we have a tricky bootstrapping + // problem. To help control static initialization order, we make sure all + // descriptors and other static data that depends on them are members of + // the outermost class in the file. This way, they will be initialized in + // a deterministic order. + + map vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + vars["index"] = SimpleItoa(descriptor_->index()); + vars["classname"] = ClassName(descriptor_); + if (descriptor_->containing_type() != NULL) { + vars["parent"] = UniqueFileScopeIdentifier(descriptor_->containing_type()); + } + if (descriptor_->file()->options().java_multiple_files()) { + // We can only make these package-private since the classes that use them + // are in separate files. + vars["private"] = ""; + } else { + vars["private"] = "private "; + } + + // The descriptor for this type. + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "$private$static final com.google.protobuf.Descriptors.Descriptor\n" + " internal_$identifier$_descriptor =\n" + " getDescriptor().getMessageTypes().get($index$);\n"); + } else { + printer->Print(vars, + "$private$static final com.google.protobuf.Descriptors.Descriptor\n" + " internal_$identifier$_descriptor =\n" + " internal_$parent$_descriptor.getNestedTypes().get($index$);\n"); + } + + // And the FieldAccessorTable. + printer->Print(vars, + "$private$static\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internal_$identifier$_fieldAccessorTable = new\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n" + " internal_$identifier$_descriptor,\n" + " new java.lang.String[] { "); + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print( + "\"$field_name$\", ", + "field_name", + UnderscoresToCapitalizedCamelCase(descriptor_->field(i))); + } + printer->Print("},\n" + " $classname$.class,\n" + " $classname$.Builder.class);\n", + "classname", ClassName(descriptor_)); + + // Generate static members for all nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + MessageGenerator(descriptor_->nested_type(i)) + .GenerateStaticVariables(printer); + } +} + +void MessageGenerator::Generate(io::Printer* printer) { + bool is_own_file = + descriptor_->containing_type() == NULL && + descriptor_->file()->options().java_multiple_files(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public $static$ final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" + " $classname$> {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + } else { + printer->Print( + "public $static$ final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + } + printer->Indent(); + printer->Print( + "// Use $classname$.newBuilder() to construct.\n" + "private $classname$() {}\n" + "\n" + "private static final $classname$ defaultInstance = new $classname$();\n" + "public static $classname$ getDefaultInstance() {\n" + " return defaultInstance;\n" + "}\n" + "\n" + "public $classname$ getDefaultInstanceForType() {\n" + " return defaultInstance;\n" + "}\n" + "\n", + "classname", descriptor_->name()); + printer->Print( + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n" + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internalGetFieldAccessorTable() {\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable;\n" + "}\n" + "\n", + "fileclass", ClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + + // Nested types and extensions + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + EnumGenerator(descriptor_->enum_type(i)).Generate(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + MessageGenerator(descriptor_->nested_type(i)).Generate(printer); + } + + for (int i = 0; i < descriptor_->extension_count(); i++) { + ExtensionGenerator(descriptor_->extension(i)).Generate(printer); + } + + // Fields + for (int i = 0; i < descriptor_->field_count(); i++) { + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)).GenerateMembers(printer); + printer->Print("\n"); + } + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateIsInitialized(printer); + GenerateMessageSerializationMethods(printer); + } + + GenerateParseFromMethods(printer); + GenerateBuilder(printer); +} + +// =================================================================== + +void MessageGenerator:: +GenerateMessageSerializationMethods(io::Printer* printer) { + scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + vector sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeOrdering()); + + printer->Print( + "public void writeTo(com.google.protobuf.CodedOutputStream output)\n" + " throws java.io.IOException {\n"); + printer->Indent(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "com.google.protobuf.GeneratedMessage.ExtendableMessage\n" + " .ExtensionWriter extensionWriter = newExtensionWriter();\n"); + } + + // Merge the fields and the extension ranges, both sorted by field number. + for (int i = 0, j = 0; + i < descriptor_->field_count() || j < sorted_extensions.size(); + ) { + if (i == descriptor_->field_count()) { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } else if (j == sorted_extensions.size()) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else if (sorted_fields[i]->number() < sorted_extensions[j]->start) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } + } + + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "getUnknownFields().writeAsMessageSetTo(output);\n"); + } else { + printer->Print( + "getUnknownFields().writeTo(output);\n"); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n" + "private int memoizedSerializedSize = -1;\n" + "public int getSerializedSize() {\n" + " int size = memoizedSerializedSize;\n" + " if (size != -1) return size;\n" + "\n" + " size = 0;\n"); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "size += extensionsSerializedSize();\n"); + } + + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "size += getUnknownFields().getSerializedSizeAsMessageSet();\n"); + } else { + printer->Print( + "size += getUnknownFields().getSerializedSize();\n"); + } + + printer->Outdent(); + printer->Print( + " memoizedSerializedSize = size;\n" + " return size;\n" + "}\n" + "\n"); +} + +void MessageGenerator:: +GenerateParseFromMethods(io::Printer* printer) { + // Note: These are separate from GenerateMessageSerializationMethods() + // because they need to be generated even for messages that are optimized + // for code size. + printer->Print( + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(byte[] data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " byte[] data,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(java.io.InputStream input)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " java.io.InputStream input,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); +} + +void MessageGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + field_generators_.get(field).GenerateSerializationCode(printer); +} + +void MessageGenerator::GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range) { + printer->Print( + "extensionWriter.writeUntil($end$, output);\n", + "end", SimpleItoa(range->end)); +} + +// =================================================================== + +void MessageGenerator::GenerateBuilder(io::Printer* printer) { + printer->Print( + "public static Builder newBuilder() { return new Builder(); }\n" + "public Builder newBuilderForType() { return new Builder(); }\n" + "public static Builder newBuilder($classname$ prototype) {\n" + " return new Builder().mergeFrom(prototype);\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n" + " $classname$, Builder> {\n", + "classname", ClassName(descriptor_)); + } else { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.Builder {\n", + "classname", ClassName(descriptor_)); + } + printer->Indent(); + + GenerateCommonBuilderMethods(printer); + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateBuilderParsingMethods(printer); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderMembers(printer); + } + + printer->Outdent(); + printer->Print("}\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +// =================================================================== + +void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { + printer->Print( + "// Construct using $classname$.newBuilder()\n" + "private Builder() {}\n" + "\n" + "$classname$ result = new $classname$();\n" + "\n" + "protected $classname$ internalGetResult() {\n" + " return result;\n" + "}\n" + "\n" + "public Builder clear() {\n" + " result = new $classname$();\n" + " return this;\n" + "}\n" + "\n" + "public Builder clone() {\n" + " return new Builder().mergeFrom(result);\n" + "}\n" + "\n" + "public com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptorForType() {\n" + " return $classname$.getDescriptor();\n" + "}\n" + "\n" + "public $classname$ getDefaultInstanceForType() {\n" + " return $classname$.getDefaultInstance();\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); + + // ----------------------------------------------------------------- + + printer->Print( + "public $classname$ build() {\n" + " if (!isInitialized()) {\n" + " throw new com.google.protobuf.UninitializedMessageException(\n" + " result);\n" + " }\n" + " return buildPartial();\n" + "}\n" + "\n" + "private $classname$ buildParsed()\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " if (!isInitialized()) {\n" + " throw new com.google.protobuf.UninitializedMessageException(\n" + " result).asInvalidProtocolBufferException();\n" + " }\n" + " return buildPartial();\n" + "}\n" + "\n" + "public $classname$ buildPartial() {\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer); + } + + printer->Outdent(); + printer->Print( + " $classname$ returnMe = result;\n" + " result = null;\n" + " return returnMe;\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); + + // ----------------------------------------------------------------- + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print( + "public Builder mergeFrom(com.google.protobuf.Message other) {\n" + " if (other instanceof $classname$) {\n" + " return mergeFrom(($classname$)other);\n" + " } else {\n" + " super.mergeFrom(other);\n" + " return this;\n" + " }\n" + "}\n" + "\n" + "public Builder mergeFrom($classname$ other) {\n" + // Optimization: If other is the default instance, we know none of its + // fields are set so we can skip the merge. + " if (other == $classname$.getDefaultInstance()) return this;\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)).GenerateMergingCode(printer); + } + + printer->Outdent(); + printer->Print( + " this.mergeUnknownFields(other.getUnknownFields());\n" + " return this;\n" + "}\n" + "\n"); + } +} + +// =================================================================== + +void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { + scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "public Builder mergeFrom(\n" + " com.google.protobuf.CodedInputStream input)\n" + " throws java.io.IOException {\n" + " return mergeFrom(input,\n" + " com.google.protobuf.ExtensionRegistry.getEmptyRegistry());\n" + "}\n" + "\n" + "public Builder mergeFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws java.io.IOException {\n"); + printer->Indent(); + + printer->Print( + "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" + " com.google.protobuf.UnknownFieldSet.newBuilder(\n" + " this.getUnknownFields());\n" + "while (true) {\n"); + printer->Indent(); + + printer->Print( + "int tag = input.readTag();\n" + "switch (tag) {\n"); + printer->Indent(); + + printer->Print( + "case 0:\n" // zero signals EOF / limit reached + " this.setUnknownFields(unknownFields.build());\n" + " return this;\n" + "default: {\n" + " if (!parseUnknownField(input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " this.setUnknownFields(unknownFields.build());\n" + " return this;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + uint32 tag = WireFormat::MakeTag(field->number(), + WireFormat::WireTypeForFieldType(field->type())); + + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + } + + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // switch (tag) + " }\n" // while (true) + "}\n" + "\n"); +} + +// =================================================================== + +void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { + printer->Print( + "public final boolean isInitialized() {\n"); + printer->Indent(); + + // Check that all required fields in this message are set. + // TODO(kenton): We can optimize this when we switch to putting all the + // "has" fields into a single bitfield. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_required()) { + printer->Print( + "if (!has$name$) return false;\n", + "name", UnderscoresToCapitalizedCamelCase(field)); + } + } + + // Now check that all embedded messages are initialized. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + switch (field->label()) { + case FieldDescriptor::LABEL_REQUIRED: + printer->Print( + "if (!get$name$().isInitialized()) return false;\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + case FieldDescriptor::LABEL_OPTIONAL: + printer->Print( + "if (has$name$()) {\n" + " if (!get$name$().isInitialized()) return false;\n" + "}\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + case FieldDescriptor::LABEL_REPEATED: + printer->Print( + "for ($type$ element : get$name$List()) {\n" + " if (!element.isInitialized()) return false;\n" + "}\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (!extensionsAreInitialized()) return false;\n"); + } + + printer->Outdent(); + printer->Print( + " return true;\n" + "}\n" + "\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h new file mode 100644 index 00000000..d9f8798e --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message.h @@ -0,0 +1,76 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class MessageGenerator { + public: + explicit MessageGenerator(const Descriptor* descriptor); + ~MessageGenerator(); + + // All static variables have to be declared at the top-level of the file + // so that we can control initialization order, which is important for + // DescriptorProto bootstrapping to work. + void GenerateStaticVariables(io::Printer* printer); + + // Generate the class itself. + void Generate(io::Printer* printer); + + private: + void GenerateMessageSerializationMethods(io::Printer* printer); + void GenerateParseFromMethods(io::Printer* printer); + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + void GenerateBuilder(io::Printer* printer); + void GenerateCommonBuilderMethods(io::Printer* printer); + void GenerateBuilderParsingMethods(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer); + + const Descriptor* descriptor_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc new file mode 100644 index 00000000..16ddb0d6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -0,0 +1,302 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetMessageVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = ClassName(descriptor->message_type()); + (*variables)["group_or_message"] = + (descriptor->type() == FieldDescriptor::TYPE_GROUP) ? + "Group" : "Message"; +} + +} // namespace + +// =================================================================== + +MessageFieldGenerator:: +MessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +MessageFieldGenerator::~MessageFieldGenerator() {} + +void MessageFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\n" + "private $type$ $name$_ = $type$.getDefaultInstance();\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" + "public $type$ get$capitalized_name$() { return $name$_; }\n"); +} + +void MessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\n" + " return result.has$capitalized_name$();\n" + "}\n" + "public $type$ get$capitalized_name$() {\n" + " return result.get$capitalized_name$();\n" + "}\n" + "public Builder set$capitalized_name$($type$ value) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = value;\n" + " return this;\n" + "}\n" + "public Builder set$capitalized_name$($type$.Builder builderForValue) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = builderForValue.build();\n" + " return this;\n" + "}\n" + "public Builder merge$capitalized_name$($type$ value) {\n" + " if (result.has$capitalized_name$() &&\n" + " result.$name$_ != $type$.getDefaultInstance()) {\n" + " result.$name$_ =\n" + " $type$.newBuilder(result.$name$_).mergeFrom(value).buildPartial();\n" + " } else {\n" + " result.$name$_ = value;\n" + " }\n" + " result.has$capitalized_name$ = true;\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.has$capitalized_name$ = false;\n" + " result.$name$_ = $type$.getDefaultInstance();\n" + " return this;\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " merge$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do for singular fields. +} + +void MessageFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = $type$.newBuilder();\n" + "if (has$capitalized_name$()) {\n" + " subBuilder.mergeFrom(get$capitalized_name$());\n" + "}\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup($number$, subBuilder, extensionRegistry);\n"); + } else { + printer->Print(variables_, + "input.readMessage(subBuilder, extensionRegistry);\n"); + } + + printer->Print(variables_, + "set$capitalized_name$(subBuilder.buildPartial());\n"); +} + +void MessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " output.write$group_or_message$($number$, get$capitalized_name$());\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, get$capitalized_name$());\n" + "}\n"); +} + +string MessageFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->message_type()); +} + +// =================================================================== + +RepeatedMessageFieldGenerator:: +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} + +void RepeatedMessageFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n" + "public int get$capitalized_name$Count() {\n" + " return result.get$capitalized_name$Count();\n" + "}\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return result.get$capitalized_name$(index);\n" + "}\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " result.$name$_.set(index, value);\n" + " return this;\n" + "}\n" + "public Builder set$capitalized_name$(int index, " + "$type$.Builder builderForValue) {\n" + " result.$name$_.set(index, builderForValue.build());\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$ value) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.add(value);\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$.Builder builderForValue) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.add(builderForValue.build());\n" + " return this;\n" + "}\n" + "public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " super.addAll(values, result.$name$_);\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.$name$_ = java.util.Collections.emptyList();\n" + " return this;\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.addAll(other.$name$_);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" + " result.$name$_ =\n" + " java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = $type$.newBuilder();\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup($number$, subBuilder, extensionRegistry);\n"); + } else { + printer->Print(variables_, + "input.readMessage(subBuilder, extensionRegistry);\n"); + } + + printer->Print(variables_, + "add$capitalized_name$(subBuilder.buildPartial());\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.write$group_or_message$($number$, element);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, element);\n" + "}\n"); +} + +string RepeatedMessageFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->message_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h new file mode 100644 index 00000000..52cf7d15 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class MessageFieldGenerator : public FieldGenerator { + public: + explicit MessageFieldGenerator(const FieldDescriptor* descriptor); + ~MessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); +}; + +class RepeatedMessageFieldGenerator : public FieldGenerator { + public: + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedMessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc new file mode 100644 index 00000000..9138f2c9 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -0,0 +1,365 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +const char* PrimitiveTypeName(JavaType type) { + switch (type) { + case JAVATYPE_INT : return "int"; + case JAVATYPE_LONG : return "long"; + case JAVATYPE_FLOAT : return "float"; + case JAVATYPE_DOUBLE : return "double"; + case JAVATYPE_BOOLEAN: return "boolean"; + case JAVATYPE_STRING : return "java.lang.String"; + case JAVATYPE_BYTES : return "com.google.protobuf.ByteString"; + case JAVATYPE_ENUM : return NULL; + case JAVATYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +const char* GetCapitalizedType(const FieldDescriptor* field) { + switch (field->type()) { + case FieldDescriptor::TYPE_INT32 : return "Int32" ; + case FieldDescriptor::TYPE_UINT32 : return "UInt32" ; + case FieldDescriptor::TYPE_SINT32 : return "SInt32" ; + case FieldDescriptor::TYPE_FIXED32 : return "Fixed32" ; + case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; + case FieldDescriptor::TYPE_INT64 : return "Int64" ; + case FieldDescriptor::TYPE_UINT64 : return "UInt64" ; + case FieldDescriptor::TYPE_SINT64 : return "SInt64" ; + case FieldDescriptor::TYPE_FIXED64 : return "Fixed64" ; + case FieldDescriptor::TYPE_SFIXED64: return "SFixed64"; + case FieldDescriptor::TYPE_FLOAT : return "Float" ; + case FieldDescriptor::TYPE_DOUBLE : return "Double" ; + case FieldDescriptor::TYPE_BOOL : return "Bool" ; + case FieldDescriptor::TYPE_STRING : return "String" ; + case FieldDescriptor::TYPE_BYTES : return "Bytes" ; + case FieldDescriptor::TYPE_ENUM : return "Enum" ; + case FieldDescriptor::TYPE_GROUP : return "Group" ; + case FieldDescriptor::TYPE_MESSAGE : return "Message" ; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +bool AllPrintableAscii(const string& text) { + // Cannot use isprint() because it's locale-specific. :( + for (int i = 0; i < text.size(); i++) { + if ((text[i] < 0x20) || text[i] >= 0x7F) { + return false; + } + } + return true; +} + +string DefaultValue(const FieldDescriptor* field) { + // Switch on cpp_type since we need to know which default_value_* method + // of FieldDescriptor to call. + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(field->default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + // Need to print as a signed int since Java has no unsigned. + return SimpleItoa(static_cast(field->default_value_uint32())); + case FieldDescriptor::CPPTYPE_INT64: + return SimpleItoa(field->default_value_int64()) + "L"; + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(static_cast(field->default_value_uint64())) + + "L"; + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field->default_value_double()) + "D"; + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field->default_value_float()) + "F"; + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + case FieldDescriptor::CPPTYPE_STRING: { + bool isBytes = field->type() == FieldDescriptor::TYPE_BYTES; + + if (!isBytes && AllPrintableAscii(field->default_value_string())) { + // All chars are ASCII and printable. In this case CEscape() works + // fine (it will only escape quotes and backslashes). + // Note: If this "optimization" is removed, DescriptorProtos will + // no longer be able to initialize itself due to bootstrapping + // problems. + return "\"" + CEscape(field->default_value_string()) + "\""; + } + + if (isBytes && !field->has_default_value()) { + return "com.google.protobuf.ByteString.EMPTY"; + } + + // Escaping strings correctly for Java and generating efficient + // initializers for ByteStrings are both tricky. We can sidestep the + // whole problem by just grabbing the default value from the descriptor. + return strings::Substitute( + "(($0) $1.getDescriptor().getFields().get($2).getDefaultValue())", + isBytes ? "com.google.protobuf.ByteString" : "java.lang.String", + ClassName(field->containing_type()), field->index()); + } + + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["default"] = DefaultValue(descriptor); + (*variables)["capitalized_type"] = GetCapitalizedType(descriptor); +} + +} // namespace + +// =================================================================== + +PrimitiveFieldGenerator:: +PrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} + +void PrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\n" + "private $type$ $name$_ = $default$;\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" + "public $type$ get$capitalized_name$() { return $name$_; }\n"); +} + +void PrimitiveFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\n" + " return result.has$capitalized_name$();\n" + "}\n" + "public $type$ get$capitalized_name$() {\n" + " return result.get$capitalized_name$();\n" + "}\n" + "public Builder set$capitalized_name$($type$ value) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = value;\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.has$capitalized_name$ = false;\n" + " result.$name$_ = $default$;\n" + " return this;\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do here for primitive types. +} + +void PrimitiveFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "set$capitalized_name$(input.read$capitalized_type$());\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " output.write$capitalized_type$($number$, get$capitalized_name$());\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size($number$, get$capitalized_name$());\n" + "}\n"); +} + +string PrimitiveFieldGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +// =================================================================== + +RepeatedPrimitiveFieldGenerator:: +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$boxed_type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n" + "public int get$capitalized_name$Count() {\n" + " return result.get$capitalized_name$Count();\n" + "}\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return result.get$capitalized_name$(index);\n" + "}\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " result.$name$_.set(index, value);\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$ value) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " }\n" + " result.$name$_.add(value);\n" + " return this;\n" + "}\n" + "public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " }\n" + " super.addAll(values, result.$name$_);\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.$name$_ = java.util.Collections.emptyList();\n" + " return this;\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " }\n" + " result.$name$_.addAll(other.$name$_);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" + " result.$name$_ =\n" + " java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "add$capitalized_name$(input.read$capitalized_type$());\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.write$capitalized_type$($number$, element);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size($number$, element);\n" + "}\n"); +} + +string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h new file mode 100644 index 00000000..6fe9177a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class PrimitiveFieldGenerator : public FieldGenerator { + public: + explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~PrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); +}; + +class RepeatedPrimitiveFieldGenerator : public FieldGenerator { + public: + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedPrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc new file mode 100644 index 00000000..8cb06270 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_service.cc @@ -0,0 +1,225 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor) + : descriptor_(descriptor) {} + +ServiceGenerator::~ServiceGenerator() {} + +void ServiceGenerator::Generate(io::Printer* printer) { + bool is_own_file = descriptor_->file()->options().java_multiple_files(); + printer->Print( + "public $static$ abstract class $classname$\n" + " implements com.google.protobuf.Service {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + printer->Indent(); + + // Generate abstract method declarations. + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["name"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "public abstract void $name$(\n" + " com.google.protobuf.RpcController controller,\n" + " $input$ request,\n" + " com.google.protobuf.RpcCallback<$output$> done);\n"); + } + + // Generate getDescriptor() and getDescriptorForType(). + printer->Print( + "\n" + "public static final\n" + " com.google.protobuf.Descriptors.ServiceDescriptor\n" + " getDescriptor() {\n" + " return $file$.getDescriptor().getServices().get($index$);\n" + "}\n" + "public final com.google.protobuf.Descriptors.ServiceDescriptor\n" + " getDescriptorForType() {\n" + " return getDescriptor();\n" + "}\n", + "file", ClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + + // Generate more stuff. + GenerateCallMethod(printer); + GenerateGetPrototype(REQUEST, printer); + GenerateGetPrototype(RESPONSE, printer); + GenerateStub(printer); + + printer->Outdent(); + printer->Print("}\n\n"); +} + +void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { + printer->Print( + "\n" + "public final void callMethod(\n" + " com.google.protobuf.Descriptors.MethodDescriptor method,\n" + " com.google.protobuf.RpcController controller,\n" + " com.google.protobuf.Message request,\n" + " com.google.protobuf.RpcCallback<\n" + " com.google.protobuf.Message> done) {\n" + " if (method.getService() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Service.callMethod() given method descriptor for wrong \" +\n" + " \"service type.\");\n" + " }\n" + " switch(method.getIndex()) {\n"); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "case $index$:\n" + " this.$method$(controller, ($input$)request,\n" + " com.google.protobuf.RpcUtil.<$output$>specializeCallback(\n" + " done));\n" + " return;\n"); + } + + printer->Print( + "default:\n" + " throw new java.lang.RuntimeException(\"Can't get here.\");\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, + io::Printer* printer) { + printer->Print( + "public final com.google.protobuf.Message\n" + " get$request_or_response$Prototype(\n" + " com.google.protobuf.Descriptors.MethodDescriptor method) {\n" + " if (method.getService() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Service.get$request_or_response$Prototype() given method \" +\n" + " \"descriptor for wrong service type.\");\n" + " }\n" + " switch(method.getIndex()) {\n", + "request_or_response", (which == REQUEST) ? "Request" : "Response"); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["type"] = ClassName( + (which == REQUEST) ? method->input_type() : method->output_type()); + printer->Print(vars, + "case $index$:\n" + " return $type$.getDefaultInstance();\n"); + } + + printer->Print( + "default:\n" + " throw new java.lang.RuntimeException(\"Can't get here.\");\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateStub(io::Printer* printer) { + printer->Print( + "public static Stub newStub(\n" + " com.google.protobuf.RpcChannel channel) {\n" + " return new Stub(channel);\n" + "}\n" + "\n" + "public static final class Stub extends $classname$ {\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + printer->Print( + "private Stub(com.google.protobuf.RpcChannel channel) {\n" + " this.channel = channel;\n" + "}\n" + "\n" + "private final com.google.protobuf.RpcChannel channel;\n" + "\n" + "public com.google.protobuf.RpcChannel getChannel() {\n" + " return channel;\n" + "}\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "\n" + "public void $method$(\n" + " com.google.protobuf.RpcController controller,\n" + " $input$ request,\n" + " com.google.protobuf.RpcCallback<$output$> done) {\n" + " channel.callMethod(\n" + " getDescriptor().getMethods().get($index$),\n" + " controller,\n" + " request,\n" + " $output$.getDefaultInstance(),\n" + " com.google.protobuf.RpcUtil.generalizeCallback(\n" + " done,\n" + " $output$.class,\n" + " $output$.getDefaultInstance()));\n" + "}\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_service.h b/src/google/protobuf/compiler/java/java_service.h new file mode 100644 index 00000000..adf3dfd4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_service.h @@ -0,0 +1,66 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_SERVICE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_SERVICE_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ServiceGenerator { + public: + explicit ServiceGenerator(const ServiceDescriptor* descriptor); + ~ServiceGenerator(); + + void Generate(io::Printer* printer); + + private: + // Generate the implementation of Service.callMethod(). + void GenerateCallMethod(io::Printer* printer); + + // Generate the implementations of Service.get{Request,Response}Prototype(). + enum RequestOrResponse { REQUEST, RESPONSE }; + void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer); + + // Generate a stub implementation of the service. + void GenerateStub(io::Printer* printer); + + const ServiceDescriptor* descriptor_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +#endif // NET_PROTO2_COMPILER_JAVA_SERVICE_H__ +} // namespace google diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc new file mode 100644 index 00000000..a5a28349 --- /dev/null +++ b/src/google/protobuf/compiler/main.cc @@ -0,0 +1,46 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include +#include + + +int main(int argc, char* argv[]) { + + google::protobuf::compiler::CommandLineInterface cli; + + // Proto2 C++ + google::protobuf::compiler::cpp::CppGenerator cpp_generator; + cli.RegisterGenerator("--cpp_out", &cpp_generator, + "Generate C++ header and source."); + + // Proto2 Java + google::protobuf::compiler::java::JavaGenerator java_generator; + cli.RegisterGenerator("--java_out", &java_generator, + "Generate Java source file."); + + + // Proto2 Python + google::protobuf::compiler::python::Generator py_generator; + cli.RegisterGenerator("--python_out", &py_generator, + "Generate Python source file."); + + return cli.Run(argc, argv); +} diff --git a/src/google/protobuf/compiler/package_info.h b/src/google/protobuf/compiler/package_info.h new file mode 100644 index 00000000..aa0c2823 --- /dev/null +++ b/src/google/protobuf/compiler/package_info.h @@ -0,0 +1,50 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file exists solely to document the google::protobuf::compiler namespace. +// It is not compiled into anything, but it may be read by an automated +// documentation generator. + +namespace google { + +namespace protobuf { + +// Implementation of the Protocol Buffer compiler. +// +// This package contains code for parsing .proto files and generating code +// based on them. There are two reasons you might be interested in this +// package: +// - You want to parse .proto files at runtime. In this case, you should +// look at importer.h. Since this functionality is widely useful, it is +// included in the libprotobuf base library; you do not have to link against +// libprotoc. +// - You want to write a custom protocol compiler which generates different +// kinds of code, e.g. code in a different language which is not supported +// by the official compiler. For this purpose, command_line_interface.h +// provides you with a complete compiler front-end, so all you need to do +// is write a custom implementation of CodeGenerator and a trivial main() +// function. You can even make your compiler support the official languages +// in addition to your own. Since this functionality is only useful to those +// writing custom compilers, it is in a separate library called "libprotoc" +// which you will have to link against. +namespace compiler {} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc new file mode 100644 index 00000000..622895ff --- /dev/null +++ b/src/google/protobuf/compiler/parser.cc @@ -0,0 +1,1105 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Recursive descent FTW. + +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +using internal::WireFormat; + +namespace { + +typedef hash_map TypeNameMap; + +TypeNameMap MakeTypeNameTable() { + TypeNameMap result; + + result["double" ] = FieldDescriptorProto::TYPE_DOUBLE; + result["float" ] = FieldDescriptorProto::TYPE_FLOAT; + result["uint64" ] = FieldDescriptorProto::TYPE_UINT64; + result["fixed64" ] = FieldDescriptorProto::TYPE_FIXED64; + result["fixed32" ] = FieldDescriptorProto::TYPE_FIXED32; + result["bool" ] = FieldDescriptorProto::TYPE_BOOL; + result["string" ] = FieldDescriptorProto::TYPE_STRING; + result["group" ] = FieldDescriptorProto::TYPE_GROUP; + + result["bytes" ] = FieldDescriptorProto::TYPE_BYTES; + result["uint32" ] = FieldDescriptorProto::TYPE_UINT32; + result["sfixed32"] = FieldDescriptorProto::TYPE_SFIXED32; + result["sfixed64"] = FieldDescriptorProto::TYPE_SFIXED64; + result["int32" ] = FieldDescriptorProto::TYPE_INT32; + result["int64" ] = FieldDescriptorProto::TYPE_INT64; + result["sint32" ] = FieldDescriptorProto::TYPE_SINT32; + result["sint64" ] = FieldDescriptorProto::TYPE_SINT64; + + return result; +} + +const TypeNameMap kTypeNames = MakeTypeNameTable(); + +} // anonymous namespace + +// Makes code slightly more readable. The meaning of "DO(foo)" is +// "Execute foo and fail if it fails.", where failure is indicated by +// returning false. +#define DO(STATEMENT) if (STATEMENT) {} else return false + +// =================================================================== + +Parser::Parser() + : input_(NULL), + error_collector_(NULL), + source_location_table_(NULL), + had_errors_(false), + require_syntax_identifier_(false) { +} + +Parser::~Parser() { +} + +// =================================================================== + +inline bool Parser::LookingAt(const char* text) { + return input_->current().text == text; +} + +inline bool Parser::LookingAtType(io::Tokenizer::TokenType token_type) { + return input_->current().type == token_type; +} + +inline bool Parser::AtEnd() { + return LookingAtType(io::Tokenizer::TYPE_END); +} + +bool Parser::TryConsume(const char* text) { + if (LookingAt(text)) { + input_->Next(); + return true; + } else { + return false; + } +} + +bool Parser::Consume(const char* text, const char* error) { + if (TryConsume(text)) { + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::Consume(const char* text) { + if (TryConsume(text)) { + return true; + } else { + AddError("Expected \"" + string(text) + "\"."); + return false; + } +} + +bool Parser::ConsumeIdentifier(string* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + *output = input_->current().text; + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeInteger(int* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + uint64 value = 0; + if (!io::Tokenizer::ParseInteger(input_->current().text, + kint32max, &value)) { + AddError("Integer out of range."); + // We still return true because we did, in fact, parse an integer. + } + *output = value; + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeInteger64(uint64 max_value, uint64* output, + const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + if (!io::Tokenizer::ParseInteger(input_->current().text, max_value, + output)) { + AddError("Integer out of range."); + // We still return true because we did, in fact, parse an integer. + *output = 0; + } + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeNumber(double* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) { + *output = io::Tokenizer::ParseFloat(input_->current().text); + input_->Next(); + return true; + } else if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + // Also accept integers. + uint64 value = 0; + if (!io::Tokenizer::ParseInteger(input_->current().text, + kuint64max, &value)) { + AddError("Integer out of range."); + // We still return true because we did, in fact, parse a number. + } + *output = value; + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeString(string* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_STRING)) { + io::Tokenizer::ParseString(input_->current().text, output); + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +// ------------------------------------------------------------------- + +void Parser::AddError(int line, int column, const string& error) { + if (error_collector_ != NULL) { + error_collector_->AddError(line, column, error); + } + had_errors_ = true; +} + +void Parser::AddError(const string& error) { + AddError(input_->current().line, input_->current().column, error); +} + +void Parser::RecordLocation( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column) { + if (source_location_table_ != NULL) { + source_location_table_->Add(descriptor, location, line, column); + } +} + +void Parser::RecordLocation( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location) { + RecordLocation(descriptor, location, + input_->current().line, input_->current().column); +} + +// ------------------------------------------------------------------- + +void Parser::SkipStatement() { + while (true) { + if (AtEnd()) { + return; + } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) { + if (TryConsume(";")) { + return; + } else if (TryConsume("{")) { + SkipRestOfBlock(); + return; + } else if (LookingAt("}")) { + return; + } + } + input_->Next(); + } +} + +void Parser::SkipRestOfBlock() { + while (true) { + if (AtEnd()) { + return; + } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) { + if (TryConsume("}")) { + return; + } else if (TryConsume("{")) { + SkipRestOfBlock(); + } + } + input_->Next(); + } +} + +// =================================================================== + +bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { + input_ = input; + had_errors_ = false; + syntax_identifier_.clear(); + + if (LookingAtType(io::Tokenizer::TYPE_START)) { + // Advance to first token. + input_->Next(); + } + + if (require_syntax_identifier_ || LookingAt("syntax")) { + if (!ParseSyntaxIdentifier()) { + // Don't attempt to parse the file if we didn't recognize the syntax + // identifier. + return false; + } + } else { + syntax_identifier_ = "proto2"; + } + + // Repeatedly parse statemetns until we reach the end of the file. + while (!AtEnd()) { + if (!ParseTopLevelStatement(file)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + + if (LookingAt("}")) { + AddError("Unmatched \"}\"."); + input_->Next(); + } + } + } + + input_ = NULL; + return !had_errors_; +} + +bool Parser::ParseSyntaxIdentifier() { + DO(Consume("syntax", "File must begin with 'syntax = \"proto2\";'.")); + DO(Consume("=")); + io::Tokenizer::Token syntax_token = input_->current(); + string syntax; + DO(ConsumeString(&syntax, "Expected syntax identifier.")); + DO(Consume(";")); + + syntax_identifier_ = syntax; + + if (syntax != "proto2") { + AddError(syntax_token.line, syntax_token.column, + "Unrecognized syntax identifier \"" + syntax + "\". This parser " + "only recognizes \"proto2\"."); + return false; + } + + return true; +} + +bool Parser::ParseTopLevelStatement(FileDescriptorProto* file) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("message")) { + return ParseMessageDefinition(file->add_message_type()); + } else if (LookingAt("enum")) { + return ParseEnumDefinition(file->add_enum_type()); + } else if (LookingAt("service")) { + return ParseServiceDefinition(file->add_service()); + } else if (LookingAt("extend")) { + return ParseExtend(file->mutable_extension(), + file->mutable_message_type()); + } else if (LookingAt("import")) { + return ParseImport(file->add_dependency()); + } else if (LookingAt("package")) { + return ParsePackage(file); + } else if (LookingAt("option")) { + return ParseOption(file->mutable_options()); + } else { + AddError("Expected top-level statement (e.g. \"message\")."); + return false; + } +} + +// ------------------------------------------------------------------- +// Messages + +bool Parser::ParseMessageDefinition(DescriptorProto* message) { + DO(Consume("message")); + RecordLocation(message, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(message->mutable_name(), "Expected message name.")); + DO(ParseMessageBlock(message)); + return true; +} + +bool Parser::ParseMessageBlock(DescriptorProto* message) { + DO(Consume("{")); + + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in message definition (missing '}')."); + return false; + } + + if (!ParseMessageStatement(message)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } + + return true; +} + +bool Parser::ParseMessageStatement(DescriptorProto* message) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("message")) { + return ParseMessageDefinition(message->add_nested_type()); + } else if (LookingAt("enum")) { + return ParseEnumDefinition(message->add_enum_type()); + } else if (LookingAt("extensions")) { + return ParseExtensions(message); + } else if (LookingAt("extend")) { + return ParseExtend(message->mutable_extension(), + message->mutable_nested_type()); + } else if (LookingAt("option")) { + return ParseOption(message->mutable_options()); + } else { + return ParseMessageField(message->add_field(), + message->mutable_nested_type()); + } +} + +bool Parser::ParseMessageField(FieldDescriptorProto* field, + RepeatedPtrField* messages) { + // Parse label and type. + FieldDescriptorProto::Label label; + DO(ParseLabel(&label)); + field->set_label(label); + + RecordLocation(field, DescriptorPool::ErrorCollector::TYPE); + FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32; + string type_name; + DO(ParseType(&type, &type_name)); + if (type_name.empty()) { + field->set_type(type); + } else { + field->set_type_name(type_name); + } + + // Parse name and '='. + RecordLocation(field, DescriptorPool::ErrorCollector::NAME); + io::Tokenizer::Token name_token = input_->current(); + DO(ConsumeIdentifier(field->mutable_name(), "Expected field name.")); + DO(Consume("=", "Missing field number.")); + + // Parse field number. + RecordLocation(field, DescriptorPool::ErrorCollector::NUMBER); + int number; + DO(ConsumeInteger(&number, "Expected field number.")); + field->set_number(number); + + // Parse options. + DO(ParseFieldOptions(field)); + + // Deal with groups. + if (type_name.empty() && type == FieldDescriptorProto::TYPE_GROUP) { + DescriptorProto* group = messages->Add(); + group->set_name(field->name()); + // Record name location to match the field name's location. + RecordLocation(group, DescriptorPool::ErrorCollector::NAME, + name_token.line, name_token.column); + + // As a hack for backwards-compatibility, we force the group name to start + // with a capital letter and lower-case the field name. New code should + // not use groups; it should use nested messages. + if (group->name()[0] < 'A' || 'Z' < group->name()[0]) { + AddError(name_token.line, name_token.column, + "Group names must start with a capital letter."); + } + LowerString(field->mutable_name()); + + field->set_type_name(group->name()); + if (LookingAt("{")) { + DO(ParseMessageBlock(group)); + } else { + AddError("Missing group body."); + return false; + } + } else { + DO(Consume(";")); + } + + return true; +} + +bool Parser::ParseFieldOptions(FieldDescriptorProto* field) { + if (!TryConsume("[")) return true; + + // Parse field options. + do { + if (LookingAt("default")) { + DO(ParseDefaultAssignment(field)); + } else { + DO(ParseOptionAssignment(field->mutable_options())); + } + } while (TryConsume(",")); + + DO(Consume("]")); + return true; +} + +bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) { + if (field->has_default_value()) { + AddError("Already set option \"default\"."); + field->clear_default_value(); + } + + DO(Consume("default")); + DO(Consume("=")); + + RecordLocation(field, DescriptorPool::ErrorCollector::DEFAULT_VALUE); + string* default_value = field->mutable_default_value(); + + if (!field->has_type()) { + // The field has a type name, but we don't know if it is a message or an + // enum yet. Assume an enum for now. + DO(ConsumeIdentifier(default_value, "Expected identifier.")); + return true; + } + + switch (field->type()) { + case FieldDescriptorProto::TYPE_INT32: + case FieldDescriptorProto::TYPE_INT64: + case FieldDescriptorProto::TYPE_SINT32: + case FieldDescriptorProto::TYPE_SINT64: + case FieldDescriptorProto::TYPE_SFIXED32: + case FieldDescriptorProto::TYPE_SFIXED64: { + uint64 max_value = kint64max; + if (field->type() == FieldDescriptorProto::TYPE_INT32 || + field->type() == FieldDescriptorProto::TYPE_SINT32 || + field->type() == FieldDescriptorProto::TYPE_SFIXED32) { + max_value = kint32max; + } + + // These types can be negative. + if (TryConsume("-")) { + default_value->append("-"); + // Two's complement always has one more negative value than positive. + ++max_value; + } + // Parse the integer to verify that it is not out-of-range. + uint64 value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + // And stringify it again. + default_value->append(SimpleItoa(value)); + break; + } + + case FieldDescriptorProto::TYPE_UINT32: + case FieldDescriptorProto::TYPE_UINT64: + case FieldDescriptorProto::TYPE_FIXED32: + case FieldDescriptorProto::TYPE_FIXED64: { + uint64 max_value = kuint64max; + if (field->type() == FieldDescriptorProto::TYPE_UINT32 || + field->type() == FieldDescriptorProto::TYPE_FIXED32) { + max_value = kuint32max; + } + + // Numeric, not negative. + if (TryConsume("-")) { + AddError("Unsigned field can't have negative default value."); + } + // Parse the integer to verify that it is not out-of-range. + uint64 value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + // And stringify it again. + default_value->append(SimpleItoa(value)); + break; + } + + case FieldDescriptorProto::TYPE_FLOAT: + case FieldDescriptorProto::TYPE_DOUBLE: + // These types can be negative. + if (TryConsume("-")) { + default_value->append("-"); + } + // Parse the integer because we have to convert hex integers to decimal + // floats. + double value; + DO(ConsumeNumber(&value, "Expected number.")); + // And stringify it again. + default_value->append(SimpleDtoa(value)); + break; + + case FieldDescriptorProto::TYPE_BOOL: + if (TryConsume("true")) { + default_value->assign("true"); + } else if (TryConsume("false")) { + default_value->assign("false"); + } else { + AddError("Expected \"true\" or \"false\"."); + return false; + } + break; + + case FieldDescriptorProto::TYPE_STRING: + DO(ConsumeString(default_value, "Expected string.")); + break; + + case FieldDescriptorProto::TYPE_BYTES: + DO(ConsumeString(default_value, "Expected string.")); + *default_value = CEscape(*default_value); + break; + + case FieldDescriptorProto::TYPE_ENUM: + DO(ConsumeIdentifier(default_value, "Expected identifier.")); + break; + + case FieldDescriptorProto::TYPE_MESSAGE: + case FieldDescriptorProto::TYPE_GROUP: + AddError("Messages can't have default values."); + return false; + } + + return true; +} + +bool Parser::ParseOptionAssignment(Message* options) { + Message::Reflection* reflection = options->GetReflection(); + const Descriptor* descriptor = options->GetDescriptor(); + + // Parse name. + string name; + int line = input_->current().line; + int column = input_->current().column; + DO(ConsumeIdentifier(&name, "Expected option name.")); + + // Is it valid? + const FieldDescriptor* field = descriptor->FindFieldByName(name); + if (field == NULL) { + AddError(line, column, "Unknown option: " + name); + return false; + } + if (field->is_repeated()) { + AddError(line, column, "Not implemented: repeated options."); + return false; + } + if (reflection->HasField(field)) { + AddError(line, column, "Option \"" + name + "\" was already set."); + return false; + } + + // Are we trying to assign a member of a message? + if (LookingAt(".")) { + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { + AddError("Option \"" + name + "\" is an atomic type, not a message."); + return false; + } + DO(Consume(".")); + + // This field is a message/group. The user must identify a field within + // it to set. + return ParseOptionAssignment(reflection->MutableMessage(field)); + } + + DO(Consume("=")); + + // Parse the option value. + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: { + uint64 value; + bool is_negative = TryConsume("-"); + uint64 max_value = kint32max; + if (is_negative) ++max_value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + reflection->SetInt32(field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_INT64: { + uint64 value; + bool is_negative = TryConsume("-"); + uint64 max_value = kint64max; + if (is_negative) ++max_value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + reflection->SetInt64(field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_UINT32: { + uint64 value; + DO(ConsumeInteger64(kuint32max, &value, "Expected integer.")); + reflection->SetUInt32(field, value); + break; + } + + case FieldDescriptor::CPPTYPE_UINT64: { + uint64 value; + DO(ConsumeInteger64(kuint64max, &value, "Expected integer.")); + reflection->SetUInt64(field, value); + break; + } + + case FieldDescriptor::CPPTYPE_DOUBLE: { + double value; + bool is_negative = TryConsume("-"); + DO(ConsumeNumber(&value, "Expected number.")); + reflection->SetDouble(field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_FLOAT: { + double value; + bool is_negative = TryConsume("-"); + DO(ConsumeNumber(&value, "Expected number.")); + reflection->SetFloat(field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_BOOL: + if (TryConsume("true")) { + reflection->SetBool(field, true); + } else if (TryConsume("false")) { + reflection->SetBool(field, false); + } else { + AddError("Expected \"true\" or \"false\"."); + return false; + } + break; + + case FieldDescriptor::CPPTYPE_ENUM: { + string value_name; + int value_line = input_->current().line; + int value_column = input_->current().column; + DO(ConsumeIdentifier(&value_name, "Expected enum value.")); + const EnumValueDescriptor* value = + field->enum_type()->FindValueByName(value_name); + if (value == NULL) { + AddError(value_line, value_column, + "Enum type \"" + field->enum_type()->full_name() + "\" has no value " + "named \"" + value_name + "\"."); + return false; + } + reflection->SetEnum(field, value); + break; + } + + case FieldDescriptor::CPPTYPE_STRING: { + string value; + DO(ConsumeString(&value, "Expected string.")); + reflection->SetString(field, value); + break; + } + + case FieldDescriptor::CPPTYPE_MESSAGE: { + // TODO(kenton): Allow use of protocol buffer text format here? + AddError("\"" + name + "\" is a message. To set fields within it, use " + "syntax like \"" + name + ".foo = value\"."); + return false; + break; + } + } + + return true; +} + +bool Parser::ParseExtensions(DescriptorProto* message) { + // Parse the declaration. + DO(Consume("extensions")); + + do { + DescriptorProto::ExtensionRange* range = message->add_extension_range(); + RecordLocation(range, DescriptorPool::ErrorCollector::NUMBER); + + int start, end; + DO(ConsumeInteger(&start, "Expected field number range.")); + + if (TryConsume("to")) { + if (TryConsume("max")) { + end = FieldDescriptor::kMaxNumber; + } else { + DO(ConsumeInteger(&end, "Expected integer.")); + } + } else { + end = start; + } + + // Users like to specify inclusive ranges, but in code we like the end + // number to be exclusive. + ++end; + + range->set_start(start); + range->set_end(end); + } while (TryConsume(",")); + + DO(Consume(";")); + return true; +} + +bool Parser::ParseExtend(RepeatedPtrField* extensions, + RepeatedPtrField* messages) { + DO(Consume("extend")); + + // We expect to see at least one extension field defined in the extend block. + // We need to create it now so we can record the extendee's location. + FieldDescriptorProto* first_field = extensions->Add(); + + // Parse the extendee type. + RecordLocation(first_field, DescriptorPool::ErrorCollector::EXTENDEE); + DO(ParseUserDefinedType(first_field->mutable_extendee())); + + // Parse the block. + DO(Consume("{")); + + bool is_first = true; + + do { + if (AtEnd()) { + AddError("Reached end of input in extend definition (missing '}')."); + return false; + } + + FieldDescriptorProto* field; + if (is_first) { + field = first_field; + is_first = false; + } else { + field = extensions->Add(); + field->set_extendee(first_field->extendee()); + } + + if (!ParseMessageField(field, messages)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } while(!TryConsume("}")); + + return true; +} + +// ------------------------------------------------------------------- +// Enums + +bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type) { + DO(Consume("enum")); + RecordLocation(enum_type, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name.")); + DO(ParseEnumBlock(enum_type)); + return true; +} + +bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type) { + DO(Consume("{")); + + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in enum definition (missing '}')."); + return false; + } + + if (!ParseEnumStatement(enum_type)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } + + return true; +} + +bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("option")) { + return ParseOption(enum_type->mutable_options()); + } else { + return ParseEnumConstant(enum_type->add_value()); + } +} + +bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value) { + RecordLocation(enum_value, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(enum_value->mutable_name(), + "Expected enum constant name.")); + DO(Consume("=", "Missing numeric value for enum constant.")); + + bool is_negative = TryConsume("-"); + int number; + DO(ConsumeInteger(&number, "Expected integer.")); + if (is_negative) number *= -1; + enum_value->set_number(number); + + // TODO(kenton): Options for enum values? + + DO(Consume(";")); + + return true; +} + +// ------------------------------------------------------------------- +// Services + +bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service) { + DO(Consume("service")); + RecordLocation(service, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(service->mutable_name(), "Expected service name.")); + DO(ParseServiceBlock(service)); + return true; +} + +bool Parser::ParseServiceBlock(ServiceDescriptorProto* service) { + DO(Consume("{")); + + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in service definition (missing '}')."); + return false; + } + + if (!ParseServiceStatement(service)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } + + return true; +} + +bool Parser::ParseServiceStatement(ServiceDescriptorProto* service) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("option")) { + return ParseOption(service->mutable_options()); + } else { + return ParseServiceMethod(service->add_method()); + } +} + +bool Parser::ParseServiceMethod(MethodDescriptorProto* method) { + DO(Consume("rpc")); + RecordLocation(method, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(method->mutable_name(), "Expected method name.")); + + // Parse input type. + DO(Consume("(")); + RecordLocation(method, DescriptorPool::ErrorCollector::INPUT_TYPE); + DO(ParseUserDefinedType(method->mutable_input_type())); + DO(Consume(")")); + + // Parse output type. + DO(Consume("returns")); + DO(Consume("(")); + RecordLocation(method, DescriptorPool::ErrorCollector::OUTPUT_TYPE); + DO(ParseUserDefinedType(method->mutable_output_type())); + DO(Consume(")")); + + if (TryConsume("{")) { + // Options! + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in method options (missing '}')."); + return false; + } + + if (TryConsume(";")) { + // empty statement; ignore + } else { + if (!ParseOption(method->mutable_options())) { + // This statement failed to parse. Skip it, but keep looping to + // parse other statements. + SkipStatement(); + } + } + } + } else { + DO(Consume(";")); + } + + return true; +} + +// ------------------------------------------------------------------- + +bool Parser::ParseLabel(FieldDescriptorProto::Label* label) { + if (TryConsume("optional")) { + *label = FieldDescriptorProto::LABEL_OPTIONAL; + return true; + } else if (TryConsume("repeated")) { + *label = FieldDescriptorProto::LABEL_REPEATED; + return true; + } else if (TryConsume("required")) { + *label = FieldDescriptorProto::LABEL_REQUIRED; + return true; + } else { + AddError("Expected \"required\", \"optional\", or \"repeated\"."); + // We can actually reasonably recover here by just assuming the user + // forgot the label altogether. + *label = FieldDescriptorProto::LABEL_OPTIONAL; + return true; + } +} + +bool Parser::ParseType(FieldDescriptorProto::Type* type, + string* type_name) { + TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text); + if (iter != kTypeNames.end()) { + *type = iter->second; + input_->Next(); + } else { + DO(ParseUserDefinedType(type_name)); + } + return true; +} + +bool Parser::ParseUserDefinedType(string* type_name) { + type_name->clear(); + + TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text); + if (iter != kTypeNames.end()) { + // Note: The only place enum types are allowed is for field types, but + // if we are parsing a field type then we would not get here because + // primitives are allowed there as well. So this error message doesn't + // need to account for enums. + AddError("Expected message type."); + + // Pretend to accept this type so that we can go on parsing. + *type_name = input_->current().text; + input_->Next(); + return true; + } + + // A leading "." means the name is fully-qualified. + if (TryConsume(".")) type_name->append("."); + + // Consume the first part of the name. + string identifier; + DO(ConsumeIdentifier(&identifier, "Expected type name.")); + type_name->append(identifier); + + // Consume more parts. + while (TryConsume(".")) { + type_name->append("."); + DO(ConsumeIdentifier(&identifier, "Expected identifier.")); + type_name->append(identifier); + } + + return true; +} + +// =================================================================== + +bool Parser::ParsePackage(FileDescriptorProto* file) { + if (file->has_package()) { + AddError("Multiple package definitions."); + } + + DO(Consume("package")); + + RecordLocation(file, DescriptorPool::ErrorCollector::NAME); + + while (true) { + string identifier; + DO(ConsumeIdentifier(&identifier, "Expected identifier.")); + file->mutable_package()->append(identifier); + if (!TryConsume(".")) break; + file->mutable_package()->append("."); + } + + DO(Consume(";")); + return true; +} + +bool Parser::ParseImport(string* import_filename) { + DO(Consume("import")); + DO(ConsumeString(import_filename, + "Expected a string naming the file to import.")); + DO(Consume(";")); + return true; +} + +bool Parser::ParseOption(Message* options) { + DO(Consume("option")); + DO(ParseOptionAssignment(options)); + DO(Consume(";")); + return true; +} + +// =================================================================== + +SourceLocationTable::SourceLocationTable() {} +SourceLocationTable::~SourceLocationTable() {} + +bool SourceLocationTable::Find( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int* line, int* column) const { + const pair* result = + FindOrNull(location_map_, make_pair(descriptor, location)); + if (result == NULL) { + *line = -1; + *column = 0; + return false; + } else { + *line = result->first; + *column = result->second; + return true; + } +} + +void SourceLocationTable::Add( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column) { + location_map_[make_pair(descriptor, location)] = make_pair(line, column); +} + +void SourceLocationTable::Clear() { + location_map_.clear(); +} + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h new file mode 100644 index 00000000..adf6e9b1 --- /dev/null +++ b/src/google/protobuf/compiler/parser.h @@ -0,0 +1,301 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Implements parsing of .proto files to FileDescriptorProtos. + +#ifndef GOOGLE_PROTOBUF_COMPILER_PARSER_H__ +#define GOOGLE_PROTOBUF_COMPILER_PARSER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { class Message; } + +namespace protobuf { +namespace compiler { + +// Defined in this file. +class Parser; +class SourceLocationTable; + +// Implements parsing of protocol definitions (such as .proto files). +// +// Note that most users will be more interested in the Importer class. +// Parser is a lower-level class which simply converts a single .proto file +// to a FileDescriptorProto. It does not resolve import directives or perform +// many other kinds of validation needed to construct a complete +// FileDescriptor. +class LIBPROTOBUF_EXPORT Parser { + public: + Parser(); + ~Parser(); + + // Parse the entire input and construct a FileDescriptorProto representing + // it. Returns true if no errors occurred, false otherwise. + bool Parse(io::Tokenizer* input, FileDescriptorProto* file); + + // Optional fetaures: + + // Requests that locations of certain definitions be recorded to the given + // SourceLocationTable while parsing. This can be used to look up exact line + // and column numbers for errors reported by DescriptorPool during validation. + // Set to NULL (the default) to discard source location information. + void RecordSourceLocationsTo(SourceLocationTable* location_table) { + source_location_table_ = location_table; + } + + // Requsets that errors be recorded to the given ErrorCollector while + // parsing. Set to NULL (the default) to discard error messages. + void RecordErrorsTo(io::ErrorCollector* error_collector) { + error_collector_ = error_collector; + } + + // Returns the identifier used in the "syntax = " declaration, if one was + // seen during the last call to Parse(), or the empty string otherwise. + const string& GetSyntaxIndentifier() { return syntax_identifier_; } + + // If set true, input files will be required to begin with a syntax + // identifier. Otherwise, files may omit this. If a syntax identifier + // is provided, it must be 'syntax = "proto2";' and must appear at the + // top of this file regardless of whether or not it was required. + void SetRequireSyntaxIdentifier(bool value) { + require_syntax_identifier_ = value; + } + + private: + // ================================================================= + // Error recovery helpers + + // Consume the rest of the current statement. This consumes tokens + // until it sees one of: + // ';' Consumes the token and returns. + // '{' Consumes the brace then calls SkipRestOfBlock(). + // '}' Returns without consuming. + // EOF Returns (can't consume). + // The Parser often calls SkipStatement() after encountering a syntax + // error. This allows it to go on parsing the following lines, allowing + // it to report more than just one error in the file. + void SkipStatement(); + + // Consume the rest of the current block, including nested blocks, + // ending after the closing '}' is encountered and consumed, or at EOF. + void SkipRestOfBlock(); + + // ----------------------------------------------------------------- + // Single-token consuming helpers + // + // These make parsing code more readable. + + // True if the current token is TYPE_END. + inline bool AtEnd(); + + // True if the next token matches the given text. + inline bool LookingAt(const char* text); + // True if the next token is of the given type. + inline bool LookingAtType(io::Tokenizer::TokenType token_type); + + // If the next token exactly matches the text given, consume it and return + // true. Otherwise, return false without logging an error. + bool TryConsume(const char* text); + + // These attempt to read some kind of token from the input. If successful, + // they return true. Otherwise they return false and add the given error + // to the error list. + + // Consume a token with the exact text given. + bool Consume(const char* text, const char* error); + // Same as above, but automatically generates the error "Expected \"text\".", + // where "text" is the expected token text. + bool Consume(const char* text); + // Consume a token of type IDENTIFIER and store its text in "output". + bool ConsumeIdentifier(string* output, const char* error); + // Consume an integer and store its value in "output". + bool ConsumeInteger(int* output, const char* error); + // Consume a 64-bit integer and store its value in "output". If the value + // is greater than max_value, an error will be reported. + bool ConsumeInteger64(uint64 max_value, uint64* output, const char* error); + // Consume a number and store its value in "output". This will accept + // tokens of either INTEGER or FLOAT type. + bool ConsumeNumber(double* output, const char* error); + // Consume a string literal and store its (unescaped) value in "output". + bool ConsumeString(string* output, const char* error); + + // ----------------------------------------------------------------- + // Error logging helpers + + // Invokes error_collector_->AddError(), if error_collector_ is not NULL. + void AddError(int line, int column, const string& error); + + // Invokes error_collector_->AddError() with the line and column number + // of the current token. + void AddError(const string& error); + + // Record the given line and column and associate it with this descriptor + // in the SourceLocationTable. + void RecordLocation(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column); + + // Record the current line and column and associate it with this descriptor + // in the SourceLocationTable. + void RecordLocation(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location); + + // ================================================================= + // Parsers for various language constructs + + // Parses the "syntax = \"proto2\";" line at the top of the file. Returns + // false if it failed to parse or if the syntax identifier was not + // recognized. + bool ParseSyntaxIdentifier(); + + // These methods parse various individual bits of code. They return + // false if they completely fail to parse the construct. In this case, + // it is probably necessary to skip the rest of the statement to recover. + // However, if these methods return true, it does NOT mean that there + // were no errors; only that there were no *syntax* errors. For instance, + // if a service method is defined using proper syntax but uses a primitive + // type as its input or output, ParseMethodField() still returns true + // and only reports the error by calling AddError(). In practice, this + // makes logic much simpler for the caller. + + // Parse a top-level message, enum, service, etc. + bool ParseTopLevelStatement(FileDescriptorProto* file); + + // Parse various language high-level language construrcts. + bool ParseMessageDefinition(DescriptorProto* message); + bool ParseEnumDefinition(EnumDescriptorProto* enum_type); + bool ParseServiceDefinition(ServiceDescriptorProto* service); + bool ParsePackage(FileDescriptorProto* file); + bool ParseImport(string* import_filename); + bool ParseOption(Message* options); + + // These methods parse the contents of a message, enum, or service type and + // add them to the given object. They consume the entire block including + // the beginning and ending brace. + bool ParseMessageBlock(DescriptorProto* message); + bool ParseEnumBlock(EnumDescriptorProto* enum_type); + bool ParseServiceBlock(ServiceDescriptorProto* service); + + // Parse one statement within a message, enum, or service block, inclunding + // final semicolon. + bool ParseMessageStatement(DescriptorProto* message); + bool ParseEnumStatement(EnumDescriptorProto* message); + bool ParseServiceStatement(ServiceDescriptorProto* message); + + // Parse a field of a message. If the field is a group, its type will be + // added to "messages". + bool ParseMessageField(FieldDescriptorProto* field, + RepeatedPtrField* messages); + + // Parse an "extensions" declaration. + bool ParseExtensions(DescriptorProto* message); + + // Parse an "extend" declaration. + bool ParseExtend(RepeatedPtrField* extensions, + RepeatedPtrField* messages); + + // Parse a single enum value within an enum block. + bool ParseEnumConstant(EnumValueDescriptorProto* enum_value); + + // Parse a single method within a service definition. + bool ParseServiceMethod(MethodDescriptorProto* method); + + // Parse "required", "optional", or "repeated" and fill in "label" + // with the value. + bool ParseLabel(FieldDescriptorProto::Label* label); + + // Parse a type name and fill in "type" (if it is a primitive) or + // "type_name" (if it is not) with the type parsed. + bool ParseType(FieldDescriptorProto::Type* type, + string* type_name); + // Parse a user-defined type and fill in "type_name" with the name. + // If a primitive type is named, it is treated as an error. + bool ParseUserDefinedType(string* type_name); + + // Parses field options, i.e. the stuff in square brackets at the end + // of a field definition. Also parses default value. + bool ParseFieldOptions(FieldDescriptorProto* field); + + // Parse the "default" option. This needs special handling because its + // type is the field's type. + bool ParseDefaultAssignment(FieldDescriptorProto* field); + + // Parse a single option name/value pair, e.g. "ctype = CORD". The name + // identifies a field of the given Message, and the value of that field + // is set to the parsed value. + bool ParseOptionAssignment(Message* options); + + // ================================================================= + + io::Tokenizer* input_; + io::ErrorCollector* error_collector_; + SourceLocationTable* source_location_table_; + bool had_errors_; + bool require_syntax_identifier_; + string syntax_identifier_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser); +}; + +// A table mapping (descriptor, ErrorLocation) pairs -- as reported by +// DescriptorPool when validating descriptors -- to line and column numbers +// within the original source code. +class LIBPROTOBUF_EXPORT SourceLocationTable { + public: + SourceLocationTable(); + ~SourceLocationTable(); + + // Finds the precise location of the given error and fills in *line and + // *column with the line and column numbers. If not found, sets *line to + // -1 and *column to 0 (since line = -1 is used to mean "error has no exact + // location" in the ErrorCollector interface). Returns true if found, false + // otherwise. + bool Find(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int* line, int* column) const; + + // Adds a location to the table. + void Add(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column); + + // Clears the contents of the table. + void Clear(); + + private: + typedef map< + pair, + pair > LocationMap; + LocationMap location_map_; +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_PARSER_H__ diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc new file mode 100644 index 00000000..489106b0 --- /dev/null +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -0,0 +1,1142 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +namespace { + +class MockErrorCollector : public io::ErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(int line, int column, const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1: $2\n", + line, column, message); + } +}; + +class MockValidationErrorCollector : public DescriptorPool::ErrorCollector { + public: + MockValidationErrorCollector(const SourceLocationTable& source_locations, + io::ErrorCollector* wrapped_collector) + : source_locations_(source_locations), + wrapped_collector_(wrapped_collector) {} + ~MockValidationErrorCollector() {} + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message) { + int line, column; + source_locations_.Find(descriptor, location, &line, &column); + wrapped_collector_->AddError(line, column, message); + } + + private: + const SourceLocationTable& source_locations_; + io::ErrorCollector* wrapped_collector_; +}; + +class ParserTest : public testing::Test { + protected: + ParserTest() + : require_syntax_identifier_(false) {} + + // Set up the parser to parse the given text. + void SetupParser(const char* text) { + raw_input_.reset(new io::ArrayInputStream(text, strlen(text))); + input_.reset(new io::Tokenizer(raw_input_.get(), &error_collector_)); + parser_.reset(new Parser()); + parser_->RecordErrorsTo(&error_collector_); + parser_->SetRequireSyntaxIdentifier(require_syntax_identifier_); + } + + // Parse the input and expect that the resulting FileDescriptorProto matches + // the given output. The output is a FileDescriptorProto in protocol buffer + // text format. + void ExpectParsesTo(const char* input, const char* output) { + SetupParser(input); + FileDescriptorProto actual, expected; + + parser_->Parse(input_.get(), &actual); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + // Parse the ASCII representation in order to canonicalize it. We could + // just compare directly to actual.DebugString(), but that would require + // that the caller precisely match the formatting that DebugString() + // produces. + ASSERT_TRUE(TextFormat::ParseFromString(output, &expected)); + + // Compare by comparing debug strings. + // TODO(kenton): Use differencer, once it is available. + EXPECT_EQ(expected.DebugString(), actual.DebugString()); + } + + // Parse the text and expect that the given errors are reported. + void ExpectHasErrors(const char* text, const char* expected_errors) { + ExpectHasEarlyExitErrors(text, expected_errors); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + } + + // Same as above but does not expect that the parser parses the complete + // input. + void ExpectHasEarlyExitErrors(const char* text, const char* expected_errors) { + SetupParser(text); + FileDescriptorProto file; + parser_->Parse(input_.get(), &file); + EXPECT_EQ(expected_errors, error_collector_.text_); + } + + // Parse the text as a file and validate it (with a DescriptorPool), and + // expect that the validation step reports the given errors. + void ExpectHasValidationErrors(const char* text, + const char* expected_errors) { + SetupParser(text); + SourceLocationTable source_locations; + parser_->RecordSourceLocationsTo(&source_locations); + + FileDescriptorProto file; + file.set_name("foo.proto"); + parser_->Parse(input_.get(), &file); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + MockValidationErrorCollector validation_error_collector( + source_locations, &error_collector_); + EXPECT_TRUE(pool_.BuildFileCollectingErrors( + file, &validation_error_collector) == NULL); + EXPECT_EQ(expected_errors, error_collector_.text_); + } + + MockErrorCollector error_collector_; + DescriptorPool pool_; + + scoped_ptr raw_input_; + scoped_ptr input_; + scoped_ptr parser_; + bool require_syntax_identifier_; +}; + +// =================================================================== + +typedef ParserTest ParseMessageTest; + +TEST_F(ParseMessageTest, SimpleMessage) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); +} + +TEST_F(ParseMessageTest, ImplicitSyntaxIdentifier) { + require_syntax_identifier_ = false; + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseMessageTest, ExplicitSyntaxIdentifier) { + ExpectParsesTo( + "syntax = \"proto2\";\n" + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseMessageTest, ExplicitRequiredSyntaxIdentifier) { + require_syntax_identifier_ = true; + ExpectParsesTo( + "syntax = \"proto2\";\n" + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseMessageTest, SimpleFields) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 15;\n" + " optional int32 bar = 34;\n" + " repeated int32 baz = 3;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:15 }" + " field { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:34 }" + " field { name:\"baz\" label:LABEL_REPEATED type:TYPE_INT32 number:3 }" + "}"); +} + +TEST_F(ParseMessageTest, PrimitiveFieldTypes) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + " required int64 foo = 1;\n" + " required uint32 foo = 1;\n" + " required uint64 foo = 1;\n" + " required sint32 foo = 1;\n" + " required sint64 foo = 1;\n" + " required fixed32 foo = 1;\n" + " required fixed64 foo = 1;\n" + " required sfixed32 foo = 1;\n" + " required sfixed64 foo = 1;\n" + " required float foo = 1;\n" + " required double foo = 1;\n" + " required string foo = 1;\n" + " required bytes foo = 1;\n" + " required bool foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_UINT32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_UINT64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SINT32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SINT64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FIXED32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FIXED64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SFIXED32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SFIXED64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FLOAT number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_DOUBLE number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_STRING number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_BYTES number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_BOOL number:1 }" + "}"); +} + +TEST_F(ParseMessageTest, FieldDefaults) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1 [default= 1 ];\n" + " required int32 foo = 1 [default= -2 ];\n" + " required int64 foo = 1 [default= 3 ];\n" + " required int64 foo = 1 [default= -4 ];\n" + " required uint32 foo = 1 [default= 5 ];\n" + " required uint64 foo = 1 [default= 6 ];\n" + " required float foo = 1 [default= 7.5];\n" + " required float foo = 1 [default= -8.5];\n" + " required float foo = 1 [default= 9 ];\n" + " required double foo = 1 [default= 10.5];\n" + " required double foo = 1 [default=-11.5];\n" + " required double foo = 1 [default= 12 ];\n" + " required string foo = 1 [default='13\\001'];\n" + " required bytes foo = 1 [default='14\\002'];\n" + " required bool foo = 1 [default=true ];\n" + " required Foo foo = 1 [default=FOO ];\n" + + " required int32 foo = 1 [default= 0x7FFFFFFF];\n" + " required int32 foo = 1 [default=-0x80000000];\n" + " required uint32 foo = 1 [default= 0xFFFFFFFF];\n" + " required int64 foo = 1 [default= 0x7FFFFFFFFFFFFFFF];\n" + " required int64 foo = 1 [default=-0x8000000000000000];\n" + " required uint64 foo = 1 [default= 0xFFFFFFFFFFFFFFFF];\n" + " required double foo = 1 [default= 0xabcd];\n" + "}\n", + +#define ETC "name:\"foo\" label:LABEL_REQUIRED number:1" + "message_type {" + " name: \"TestMessage\"" + " field { type:TYPE_INT32 default_value:\"1\" "ETC" }" + " field { type:TYPE_INT32 default_value:\"-2\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"3\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"-4\" "ETC" }" + " field { type:TYPE_UINT32 default_value:\"5\" "ETC" }" + " field { type:TYPE_UINT64 default_value:\"6\" "ETC" }" + " field { type:TYPE_FLOAT default_value:\"7.5\" "ETC" }" + " field { type:TYPE_FLOAT default_value:\"-8.5\" "ETC" }" + " field { type:TYPE_FLOAT default_value:\"9\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"10.5\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"-11.5\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"12\" "ETC" }" + " field { type:TYPE_STRING default_value:\"13\\001\" "ETC" }" + " field { type:TYPE_BYTES default_value:\"14\\\\002\" "ETC" }" + " field { type:TYPE_BOOL default_value:\"true\" "ETC" }" + " field { type_name:\"Foo\" default_value:\"FOO\" "ETC" }" + + " field { type:TYPE_INT32 default_value:\"2147483647\" "ETC" }" + " field { type:TYPE_INT32 default_value:\"-2147483648\" "ETC" }" + " field { type:TYPE_UINT32 default_value:\"4294967295\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"9223372036854775807\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"-9223372036854775808\" "ETC" }" + " field { type:TYPE_UINT64 default_value:\"18446744073709551615\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"43981\" "ETC" }" + "}"); +#undef ETC +} + +TEST_F(ParseMessageTest, FieldOptions) { + ExpectParsesTo( + "message TestMessage {\n" + " optional string foo = 1 [ctype=CORD];\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_STRING number:1" + " options { ctype:CORD } }" + "}"); +} + +TEST_F(ParseMessageTest, Group) { + ExpectParsesTo( + "message TestMessage {\n" + " optional group TestGroup = 1 {};\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " nested_type { name: \"TestGroup\" }" + " field { name:\"testgroup\" label:LABEL_OPTIONAL number:1" + " type:TYPE_GROUP type_name: \"TestGroup\" }" + "}"); +} + +TEST_F(ParseMessageTest, NestedMessage) { + ExpectParsesTo( + "message TestMessage {\n" + " message Nested {}\n" + " optional Nested test_nested = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " nested_type { name: \"Nested\" }" + " field { name:\"test_nested\" label:LABEL_OPTIONAL number:1" + " type_name: \"Nested\" }" + "}"); +} + +TEST_F(ParseMessageTest, NestedEnum) { + ExpectParsesTo( + "message TestMessage {\n" + " enum NestedEnum {}\n" + " optional NestedEnum test_enum = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " enum_type { name: \"NestedEnum\" }" + " field { name:\"test_enum\" label:LABEL_OPTIONAL number:1" + " type_name: \"NestedEnum\" }" + "}"); +} + +TEST_F(ParseMessageTest, ExtensionRange) { + ExpectParsesTo( + "message TestMessage {\n" + " extensions 10 to 19;\n" + " extensions 30 to max;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " extension_range { start:10 end:20 }" + " extension_range { start:30 end:536870912 }" + "}"); +} + +TEST_F(ParseMessageTest, CompoundExtensionRange) { + ExpectParsesTo( + "message TestMessage {\n" + " extensions 2, 15, 9 to 11, 100 to max, 3;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " extension_range { start:2 end:3 }" + " extension_range { start:15 end:16 }" + " extension_range { start:9 end:12 }" + " extension_range { start:100 end:536870912 }" + " extension_range { start:3 end:4 }" + "}"); +} + +TEST_F(ParseMessageTest, Extensions) { + ExpectParsesTo( + "extend Extendee1 { optional int32 foo = 12; }\n" + "extend Extendee2 { repeated TestMessage bar = 22; }\n", + + "extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12" + " extendee: \"Extendee1\" } " + "extension { name:\"bar\" label:LABEL_REPEATED number:22" + " type_name:\"TestMessage\" extendee: \"Extendee2\" }"); +} + +TEST_F(ParseMessageTest, ExtensionsInMessageScope) { + ExpectParsesTo( + "message TestMessage {\n" + " extend Extendee1 { optional int32 foo = 12; }\n" + " extend Extendee2 { repeated TestMessage bar = 22; }\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12" + " extendee: \"Extendee1\" }" + " extension { name:\"bar\" label:LABEL_REPEATED number:22" + " type_name:\"TestMessage\" extendee: \"Extendee2\" }" + "}"); +} + +TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) { + ExpectParsesTo( + "extend Extendee1 {\n" + " optional int32 foo = 12;\n" + " repeated TestMessage bar = 22;\n" + "}\n", + + "extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12" + " extendee: \"Extendee1\" } " + "extension { name:\"bar\" label:LABEL_REPEATED number:22" + " type_name:\"TestMessage\" extendee: \"Extendee1\" }"); +} + +// =================================================================== + +typedef ParserTest ParseEnumTest; + +TEST_F(ParseEnumTest, SimpleEnum) { + ExpectParsesTo( + "enum TestEnum {\n" + " FOO = 0;\n" + "}\n", + + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"FOO\" number:0 }" + "}"); +} + +TEST_F(ParseEnumTest, Values) { + ExpectParsesTo( + "enum TestEnum {\n" + " FOO = 13;\n" + " BAR = -10;\n" + " BAZ = 500;\n" + "}\n", + + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"FOO\" number:13 }" + " value { name:\"BAR\" number:-10 }" + " value { name:\"BAZ\" number:500 }" + "}"); +} + +// =================================================================== + +typedef ParserTest ParseServiceTest; + +TEST_F(ParseServiceTest, SimpleService) { + ExpectParsesTo( + "service TestService {\n" + " rpc Foo(In) returns (Out);\n" + "}\n", + + "service {" + " name: \"TestService\"" + " method { name:\"Foo\" input_type:\"In\" output_type:\"Out\" }" + "}"); +} + +TEST_F(ParseServiceTest, Methods) { + ExpectParsesTo( + "service TestService {\n" + " rpc Foo(In1) returns (Out1);\n" + " rpc Bar(In2) returns (Out2);\n" + " rpc Baz(In3) returns (Out3);\n" + "}\n", + + "service {" + " name: \"TestService\"" + " method { name:\"Foo\" input_type:\"In1\" output_type:\"Out1\" }" + " method { name:\"Bar\" input_type:\"In2\" output_type:\"Out2\" }" + " method { name:\"Baz\" input_type:\"In3\" output_type:\"Out3\" }" + "}"); +} + +// =================================================================== +// imports and packages + +typedef ParserTest ParseMiscTest; + +TEST_F(ParseMiscTest, ParseImport) { + ExpectParsesTo( + "import \"foo/bar/baz.proto\";\n", + "dependency: \"foo/bar/baz.proto\""); +} + +TEST_F(ParseMiscTest, ParseMultipleImports) { + ExpectParsesTo( + "import \"foo.proto\";\n" + "import \"bar.proto\";\n" + "import \"baz.proto\";\n", + "dependency: \"foo.proto\"" + "dependency: \"bar.proto\"" + "dependency: \"baz.proto\""); +} + +TEST_F(ParseMiscTest, ParsePackage) { + ExpectParsesTo( + "package foo.bar.baz;\n", + "package: \"foo.bar.baz\""); +} + +TEST_F(ParseMiscTest, ParsePackageWithSpaces) { + ExpectParsesTo( + "package foo . bar. \n" + " baz;\n", + "package: \"foo.bar.baz\""); +} + +// =================================================================== +// options + +TEST_F(ParseMiscTest, ParseFileOptions) { + ExpectParsesTo( + "option java_package = \"com.google.foo\";\n" + "option optimize_for = CODE_SIZE;", + + "options {" + " java_package: \"com.google.foo\"" + " optimize_for: CODE_SIZE" + "}"); +} + +// TODO(kenton): We'd like to be able to test all possible option types, +// but we are unable to do so here because we can only test the options +// that actually exist, which currently doesn't cover everything. Perhaps +// we can solve this in the future by allowing options to be extended, then +// defining extensions of every type? + +// =================================================================== +// Error tests +// +// There are a very large number of possible errors that the parser could +// report, so it's infeasible to test every single one of them. Instead, +// we test each unique call to AddError() in parser.h. This does not mean +// we are testing every possible error that Parser can generate because +// each variant of the Consume() helper only counts as one unique call to +// AddError(). + +typedef ParserTest ParseErrorTest; + +TEST_F(ParseErrorTest, MissingSyntaxIdentifier) { + require_syntax_identifier_ = true; + ExpectHasEarlyExitErrors( + "message TestMessage {}", + "0:0: File must begin with 'syntax = \"proto2\";'.\n"); + EXPECT_EQ("", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseErrorTest, UnknownSyntaxIdentifier) { + ExpectHasEarlyExitErrors( + "syntax = \"no_such_syntax\";", + "0:9: Unrecognized syntax identifier \"no_such_syntax\". This parser " + "only recognizes \"proto2\".\n"); + EXPECT_EQ("no_such_syntax", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseErrorTest, SimpleSyntaxError) { + ExpectHasErrors( + "message TestMessage @#$ { blah }", + "0:20: Expected \"{\".\n"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseErrorTest, ExpectedTopLevel) { + ExpectHasErrors( + "blah;", + "0:0: Expected top-level statement (e.g. \"message\").\n"); +} + +TEST_F(ParseErrorTest, UnmatchedCloseBrace) { + // This used to cause an infinite loop. Doh. + ExpectHasErrors( + "}", + "0:0: Expected top-level statement (e.g. \"message\").\n" + "0:0: Unmatched \"}\".\n"); +} + +// ------------------------------------------------------------------- +// Message errors + +TEST_F(ParseErrorTest, MessageMissingName) { + ExpectHasErrors( + "message {}", + "0:8: Expected message name.\n"); +} + +TEST_F(ParseErrorTest, MessageMissingBody) { + ExpectHasErrors( + "message TestMessage;", + "0:19: Expected \"{\".\n"); +} + +TEST_F(ParseErrorTest, EofInMessage) { + ExpectHasErrors( + "message TestMessage {", + "0:21: Reached end of input in message definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, MissingFieldNumber) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo;\n" + "}\n", + "1:20: Missing field number.\n"); +} + +TEST_F(ParseErrorTest, ExpectedFieldNumber) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo = ;\n" + "}\n", + "1:23: Expected field number.\n"); +} + +TEST_F(ParseErrorTest, FieldNumberOutOfRange) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo = 0x100000000;\n" + "}\n", + "1:23: Integer out of range.\n"); +} + +TEST_F(ParseErrorTest, MissingLabel) { + ExpectHasErrors( + "message TestMessage {\n" + " int32 foo = 1;\n" + "}\n", + "1:2: Expected \"required\", \"optional\", or \"repeated\".\n"); +} + +TEST_F(ParseErrorTest, ExpectedOptionName) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [];\n" + "}\n", + "1:27: Expected option name.\n"); +} + +TEST_F(ParseErrorTest, UnknownOption) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [nosuchoption=5];\n" + "}\n", + "1:27: Unknown option: nosuchoption\n"); +} + +TEST_F(ParseErrorTest, DefaultValueTypeMismatch) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=true];\n" + "}\n", + "1:35: Expected integer.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueNotBoolean) { + ExpectHasErrors( + "message TestMessage {\n" + " optional bool foo = 1 [default=blah];\n" + "}\n", + "1:33: Expected \"true\" or \"false\".\n"); +} + +TEST_F(ParseErrorTest, DefaultValueNotString) { + ExpectHasErrors( + "message TestMessage {\n" + " optional string foo = 1 [default=1];\n" + "}\n", + "1:35: Expected string.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueUnsignedNegative) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=-1];\n" + "}\n", + "1:36: Unsigned field can't have negative default value.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueTooLarge) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo = 1 [default= 0x80000000];\n" + " optional int32 foo = 1 [default=-0x80000001];\n" + " optional uint32 foo = 1 [default= 0x100000000];\n" + " optional int64 foo = 1 [default= 0x80000000000000000];\n" + " optional int64 foo = 1 [default=-0x80000000000000001];\n" + " optional uint64 foo = 1 [default= 0x100000000000000000];\n" + "}\n", + "1:36: Integer out of range.\n" + "2:36: Integer out of range.\n" + "3:36: Integer out of range.\n" + "4:36: Integer out of range.\n" + "5:36: Integer out of range.\n" + "6:36: Integer out of range.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueMissing) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=];\n" + "}\n", + "1:35: Expected integer.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueForGroup) { + ExpectHasErrors( + "message TestMessage {\n" + " optional group Foo = 1 [default=blah] {}\n" + "}\n", + "1:34: Messages can't have default values.\n"); +} + +TEST_F(ParseErrorTest, DuplicateDefaultValue) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=1,default=2];\n" + "}\n", + "1:37: Already set option \"default\".\n"); +} + +TEST_F(ParseErrorTest, GroupNotCapitalized) { + ExpectHasErrors( + "message TestMessage {\n" + " optional group foo = 1 {}\n" + "}\n", + "1:17: Group names must start with a capital letter.\n"); +} + +TEST_F(ParseErrorTest, GroupMissingBody) { + ExpectHasErrors( + "message TestMessage {\n" + " optional group Foo = 1;\n" + "}\n", + "1:24: Missing group body.\n"); +} + +TEST_F(ParseErrorTest, ExtendingPrimitive) { + ExpectHasErrors( + "extend int32 { optional string foo = 4; }\n", + "0:7: Expected message type.\n"); +} + +TEST_F(ParseErrorTest, ErrorInExtension) { + ExpectHasErrors( + "message Foo { extensions 100 to 199; }\n" + "extend Foo { optional string foo; }\n", + "1:32: Missing field number.\n"); +} + +TEST_F(ParseErrorTest, MultipleParseErrors) { + // When a statement has a parse error, the parser should be able to continue + // parsing at the next statement. + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo;\n" + " !invalid statement ending in a block { blah blah { blah } blah }\n" + " optional int32 bar = 3 {}\n" + "}\n", + "1:20: Missing field number.\n" + "2:2: Expected \"required\", \"optional\", or \"repeated\".\n" + "2:2: Expected type name.\n" + "3:25: Expected \";\".\n"); +} + +// ------------------------------------------------------------------- +// Enum errors + +TEST_F(ParseErrorTest, EofInEnum) { + ExpectHasErrors( + "enum TestEnum {", + "0:15: Reached end of input in enum definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, EnumValueMissingNumber) { + ExpectHasErrors( + "enum TestEnum {\n" + " FOO;\n" + "}\n", + "1:5: Missing numeric value for enum constant.\n"); +} + +// ------------------------------------------------------------------- +// Service errors + +TEST_F(ParseErrorTest, EofInService) { + ExpectHasErrors( + "service TestService {", + "0:21: Reached end of input in service definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, ServiceMethodPrimitiveParams) { + ExpectHasErrors( + "service TestService {\n" + " rpc Foo(int32) returns (string);\n" + "}\n", + "1:10: Expected message type.\n" + "1:26: Expected message type.\n"); +} + +TEST_F(ParseErrorTest, EofInMethodOptions) { + ExpectHasErrors( + "service TestService {\n" + " rpc Foo(Bar) returns(Bar) {", + "1:29: Reached end of input in method options (missing '}').\n" + "1:29: Reached end of input in service definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, PrimitiveMethodInput) { + ExpectHasErrors( + "service TestService {\n" + " rpc Foo(int32) returns(Bar);\n" + "}\n", + "1:10: Expected message type.\n"); +} + +TEST_F(ParseErrorTest, MethodOptionTypeError) { + // This used to cause an infinite loop. + ExpectHasErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Baz) returns(Baz) { option invalid syntax; }\n" + "}\n", + "2:37: Unknown option: invalid\n"); +} + +// ------------------------------------------------------------------- +// Import and package errors + +TEST_F(ParseErrorTest, ImportNotQuoted) { + ExpectHasErrors( + "import foo;\n", + "0:7: Expected a string naming the file to import.\n"); +} + +TEST_F(ParseErrorTest, MultiplePackagesInFile) { + ExpectHasErrors( + "package foo;\n" + "package bar;\n", + "1:0: Multiple package definitions.\n"); +} + +// ------------------------------------------------------------------- +// Option errors + +TEST_F(ParseErrorTest, OptionWrongType) { + ExpectHasErrors( + "message TestMessage {\n" + " optional string foo = 1 [ctype=1];\n" + "}\n", + "1:33: Expected enum value.\n"); +} + +TEST_F(ParseErrorTest, DupOption) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [ctype=CORD,ctype=CORD];\n" + "}\n", + "1:38: Option \"ctype\" was already set.\n"); +} + +TEST_F(ParseErrorTest, NotMessageOption) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [ctype.blah=1];\n" + "}\n", + "1:32: Option \"ctype\" is an atomic type, not a message.\n"); +} + +// TODO(kenton): Test errors for all possible option types (see TODO above, +// under the option parsing tests). + +// =================================================================== +// Test that errors detected by DescriptorPool correctly report line and +// column numbers. We have one test for every call to RecordLocation() in +// parser.cc. + +typedef ParserTest ParserValidationErrorTest; + +TEST_F(ParserValidationErrorTest, PackageNameError) { + // Create another file which defines symbol "foo". + FileDescriptorProto other_file; + other_file.set_name("bar.proto"); + other_file.add_message_type()->set_name("foo"); + EXPECT_TRUE(pool_.BuildFile(other_file) != NULL); + + // Now try to define it as a package. + ExpectHasValidationErrors( + "package foo.bar;", + "0:8: \"foo\" is already defined (as something other than a package) " + "in file \"bar.proto\".\n"); +} + +TEST_F(ParserValidationErrorTest, MessageNameError) { + ExpectHasValidationErrors( + "message Foo {}\n" + "message Foo {}\n", + "1:8: \"Foo\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldNameError) { + ExpectHasValidationErrors( + "message Foo {\n" + " optional int32 bar = 1;\n" + " optional int32 bar = 2;\n" + "}\n", + "2:17: \"bar\" is already defined in \"Foo\".\n"); +} + +TEST_F(ParserValidationErrorTest, FieldTypeError) { + ExpectHasValidationErrors( + "message Foo {\n" + " optional Baz bar = 1;\n" + "}\n", + "1:11: \"Baz\" is not defined.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldNumberError) { + ExpectHasValidationErrors( + "message Foo {\n" + " optional int32 bar = 0;\n" + "}\n", + "1:23: Field numbers must be positive integers.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldExtendeeError) { + ExpectHasValidationErrors( + "extend Baz { optional int32 bar = 1; }\n", + "0:7: \"Baz\" is not defined.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldDefaultValueError) { + ExpectHasValidationErrors( + "enum Baz { QUX = 1; }\n" + "message Foo {\n" + " optional Baz bar = 1 [default=NO_SUCH_VALUE];\n" + "}\n", + "2:32: Enum type \"Baz\" has no value named \"NO_SUCH_VALUE\".\n"); +} + +TEST_F(ParserValidationErrorTest, ExtensionRangeNumberError) { + ExpectHasValidationErrors( + "message Foo {\n" + " extensions 0;\n" + "}\n", + "1:13: Extension numbers must be positive integers.\n"); +} + +TEST_F(ParserValidationErrorTest, EnumNameError) { + ExpectHasValidationErrors( + "enum Foo {A = 1;}\n" + "enum Foo {B = 1;}\n", + "1:5: \"Foo\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, EnumValueNameError) { + ExpectHasValidationErrors( + "enum Foo {\n" + " BAR = 1;\n" + " BAR = 1;\n" + "}\n", + "2:2: \"BAR\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, ServiceNameError) { + ExpectHasValidationErrors( + "service Foo {}\n" + "service Foo {}\n", + "1:8: \"Foo\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, MethodNameError) { + ExpectHasValidationErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Baz) returns(Baz);\n" + " rpc Bar(Baz) returns(Baz);\n" + "}\n", + "3:6: \"Bar\" is already defined in \"Foo\".\n"); +} + +TEST_F(ParserValidationErrorTest, MethodInputTypeError) { + ExpectHasValidationErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Qux) returns(Baz);\n" + "}\n", + "2:10: \"Qux\" is not defined.\n"); +} + +TEST_F(ParserValidationErrorTest, MethodOutputTypeError) { + ExpectHasValidationErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Baz) returns(Qux);\n" + "}\n", + "2:23: \"Qux\" is not defined.\n"); +} + +// =================================================================== +// Test that the output from FileDescriptor::DebugString() (and all other +// descriptor types) is parseable, and results in the same Descriptor +// definitions again afoter parsing (not, however, that the order of messages +// cannot be guaranteed to be the same) + +typedef ParserTest ParseDecriptorDebugTest; + +class CompareDescriptorNames { + public: + bool operator()(const DescriptorProto* left, const DescriptorProto* right) { + return left->name() < right->name(); + } +}; + +// Sorts nested DescriptorProtos of a DescriptoProto, by name. +void SortMessages(DescriptorProto *descriptor_proto) { + int size = descriptor_proto->nested_type_size(); + // recursively sort; we can't guarantee the order of nested messages either + for (int i = 0; i < size; ++i) { + SortMessages(descriptor_proto->mutable_nested_type(i)); + } + DescriptorProto **data = + descriptor_proto->mutable_nested_type()->mutable_data(); + sort(data, data + size, CompareDescriptorNames()); +} + +// Sorts DescriptorProtos belonging to a FileDescriptorProto, by name. +void SortMessages(FileDescriptorProto *file_descriptor_proto) { + int size = file_descriptor_proto->message_type_size(); + // recursively sort; we can't guarantee the order of nested messages either + for (int i = 0; i < size; ++i) { + SortMessages(file_descriptor_proto->mutable_message_type(i)); + } + DescriptorProto **data = + file_descriptor_proto->mutable_message_type()->mutable_data(); + sort(data, data + size, CompareDescriptorNames()); +} + +TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { + const FileDescriptor* original_file = + protobuf_unittest::TestAllTypes::descriptor()->file(); + FileDescriptorProto expected; + original_file->CopyTo(&expected); + + // Get the DebugString of the unittest.proto FileDecriptor, which includes + // all other descriptor types + string debug_string = original_file->DebugString(); + + // Parse the debug string + SetupParser(debug_string.c_str()); + FileDescriptorProto parsed; + parser_->Parse(input_.get(), &parsed); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + // We now have a FileDescriptorProto, but to compare with the expected we + // need to link to a FileDecriptor, then output back to a proto. We'll + // also need to give it the same name as the original. + parsed.set_name("google/protobuf/unittest.proto"); + // We need the imported dependency before we can build our parsed proto + const FileDescriptor* import = + protobuf_unittest_import::ImportMessage::descriptor()->file(); + FileDescriptorProto import_proto; + import->CopyTo(&import_proto); + ASSERT_TRUE(pool_.BuildFile(import_proto) != NULL); + const FileDescriptor* actual = pool_.BuildFile(parsed); + parsed.Clear(); + actual->CopyTo(&parsed); + ASSERT_TRUE(actual != NULL); + + // The messages might be in different orders, making them hard to compare. + // So, sort the messages in the descriptor protos (including nested messages, + // recursively). + SortMessages(&expected); + SortMessages(&parsed); + + // I really wanted to use StringDiff here for the debug output on fail, + // but the strings are too long for it, and if I increase its max size, + // we get a memory allocation failure :( + EXPECT_EQ(expected.DebugString(), parsed.DebugString()); +} + +// =================================================================== + +} // anonymous namespace + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc new file mode 100644 index 00000000..e1171382 --- /dev/null +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -0,0 +1,794 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: robinson@google.com (Will Robinson) +// +// This module outputs pure-Python protocol message classes that will +// largely be constructed at runtime via the metaclass in reflection.py. +// In other words, our job is basically to output a Python equivalent +// of the C++ *Descriptor objects, and fix up all circular references +// within these objects. +// +// Note that the runtime performance of protocol message classes created in +// this way is expected to be lousy. The plan is to create an alternate +// generator that outputs a Python/C extension module that lets +// performance-minded Python code leverage the fast C++ implementation +// directly. + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace python { + +namespace { + +// Returns a copy of |filename| with any trailing ".protodevel" or ".proto +// suffix stripped. +// TODO(robinson): Unify with copy in compiler/cpp/internal/helpers.cc. +string StripProto(const string& filename) { + const char* suffix = HasSuffixString(filename, ".protodevel") + ? ".protodevel" : ".proto"; + return StripSuffixString(filename, suffix); +} + + +// Returns the Python module name expected for a given .proto filename. +string ModuleName(const string& filename) { + string basename = StripProto(filename); + StripString(&basename, "-", '_'); + StripString(&basename, "/", '.'); + return basename + "_pb2"; +} + + +// Returns the name of all containing types for descriptor, +// in order from outermost to innermost, followed by descriptor's +// own name. Each name is separated by |separator|. +template +string NamePrefixedWithNestedTypes(const DescriptorT& descriptor, + const string& separator) { + string name = descriptor.name(); + for (const Descriptor* current = descriptor.containing_type(); + current != NULL; current = current->containing_type()) { + name = current->name() + separator + name; + } + return name; +} + + +// Name of the class attribute where we store the Python +// descriptor.Descriptor instance for the generated class. +// Must stay consistent with the _DESCRIPTOR_KEY constant +// in proto2/public/reflection.py. +const char kDescriptorKey[] = "DESCRIPTOR"; + + +// Prints the common boilerplate needed at the top of every .py +// file output by this generator. +void PrintTopBoilerplate( + io::Printer* printer, const FileDescriptor* file, bool descriptor_proto) { + // TODO(robinson): Allow parameterization of Python version? + printer->Print( + "#!/usr/bin/python2.4\n" + "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n" + "from google.protobuf import descriptor\n" + "from google.protobuf import message\n" + "from google.protobuf import reflection\n" + "from google.protobuf import service\n" + "from google.protobuf import service_reflection\n"); + // Avoid circular imports if this module is descriptor_pb2. + if (!descriptor_proto) { + printer->Print( + "from google.protobuf import descriptor_pb2\n"); + } +} + + +// Returns a Python literal giving the default value for a field. +// If the field specifies no explicit default value, we'll return +// the default default value for the field type (zero for numbers, +// empty string for strings, empty list for repeated fields, and +// None for non-repeated, composite fields). +// +// TODO(robinson): Unify with code from +// //compiler/cpp/internal/primitive_field.cc +// //compiler/cpp/internal/enum_field.cc +// //compiler/cpp/internal/string_field.cc +string StringifyDefaultValue(const FieldDescriptor& field) { + if (field.is_repeated()) { + return "[]"; + } + + switch (field.cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(field.default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + return SimpleItoa(field.default_value_uint32()); + case FieldDescriptor::CPPTYPE_INT64: + return SimpleItoa(field.default_value_int64()); + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(field.default_value_uint64()); + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field.default_value_double()); + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field.default_value_float()); + case FieldDescriptor::CPPTYPE_BOOL: + return field.default_value_bool() ? "True" : "False"; + case FieldDescriptor::CPPTYPE_ENUM: + return SimpleItoa(field.default_value_enum()->number()); + case FieldDescriptor::CPPTYPE_STRING: + return "\"" + CEscape(field.default_value_string()) + "\""; + case FieldDescriptor::CPPTYPE_MESSAGE: + return "None"; + } + // (We could add a default case above but then we wouldn't get the nice + // compiler warning when a new type is added.) + GOOGLE_LOG(FATAL) << "Not reached."; + return ""; +} + + + +} // namespace + + +Generator::Generator() : file_(NULL) { +} + +Generator::~Generator() { +} + +bool Generator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + + // Completely serialize all Generate() calls on this instance. The + // thread-safety constraints of the CodeGenerator interface aren't clear so + // just be as conservative as possible. It's easier to relax this later if + // we need to, but I doubt it will be an issue. + // TODO(kenton): The proper thing to do would be to allocate any state on + // the stack and use that, so that the Generator class itself does not need + // to have any mutable members. Then it is implicitly thread-safe. + MutexLock lock(&mutex_); + file_ = file; + string module_name = ModuleName(file->name()); + string filename = module_name; + StripString(&filename, ".", '/'); + filename += ".py"; + + + scoped_ptr output(output_directory->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); + printer_ = &printer; + + PrintTopBoilerplate(printer_, file_, GeneratingDescriptorProto()); + PrintTopLevelEnums(); + PrintTopLevelExtensions(); + PrintAllNestedEnumsInFile(); + PrintMessageDescriptors(); + // We have to print the imports after the descriptors, so that mutually + // recursive protos in separate files can successfully reference each other. + PrintImports(); + FixForeignFieldsInDescriptors(); + PrintMessages(); + // We have to fix up the extensions after the message classes themselves, + // since they need to call static RegisterExtension() methods on these + // classes. + FixForeignFieldsInExtensions(); + PrintServices(); + return !printer.failed(); +} + +// Prints Python imports for all modules imported by |file|. +void Generator::PrintImports() const { + for (int i = 0; i < file_->dependency_count(); ++i) { + string module_name = ModuleName(file_->dependency(i)->name()); + printer_->Print("import $module$\n", "module", + module_name); + } + printer_->Print("\n"); +} + +// Prints descriptors and module-level constants for all top-level +// enums defined in |file|. +void Generator::PrintTopLevelEnums() const { + vector > top_level_enum_values; + for (int i = 0; i < file_->enum_type_count(); ++i) { + const EnumDescriptor& enum_descriptor = *file_->enum_type(i); + PrintEnum(enum_descriptor); + printer_->Print("\n"); + + for (int j = 0; j < enum_descriptor.value_count(); ++j) { + const EnumValueDescriptor& value_descriptor = *enum_descriptor.value(j); + top_level_enum_values.push_back( + make_pair(value_descriptor.name(), value_descriptor.number())); + } + } + + for (int i = 0; i < top_level_enum_values.size(); ++i) { + printer_->Print("$name$ = $value$\n", + "name", top_level_enum_values[i].first, + "value", SimpleItoa(top_level_enum_values[i].second)); + } + printer_->Print("\n"); +} + +// Prints all enums contained in all message types in |file|. +void Generator::PrintAllNestedEnumsInFile() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + PrintNestedEnums(*file_->message_type(i)); + } +} + +// Prints a Python statement assigning the appropriate module-level +// enum name to a Python EnumDescriptor object equivalent to +// enum_descriptor. +void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const { + map m; + m["descriptor_name"] = ModuleLevelDescriptorName(enum_descriptor); + m["name"] = enum_descriptor.name(); + m["full_name"] = enum_descriptor.full_name(); + m["filename"] = enum_descriptor.name(); + const char enum_descriptor_template[] = + "$descriptor_name$ = descriptor.EnumDescriptor(\n" + " name='$name$',\n" + " full_name='$full_name$',\n" + " filename='$filename$',\n" + " values=[\n"; + string options_string; + enum_descriptor.options().SerializeToString(&options_string); + printer_->Print(m, enum_descriptor_template); + printer_->Indent(); + printer_->Indent(); + for (int i = 0; i < enum_descriptor.value_count(); ++i) { + PrintEnumValueDescriptor(*enum_descriptor.value(i)); + printer_->Print(",\n"); + } + printer_->Outdent(); + printer_->Print("],\n"); + printer_->Print("options=$options_value$,\n", + "options_value", + OptionsValue("EnumOptions", CEscape(options_string))); + printer_->Outdent(); + printer_->Print(")\n"); + printer_->Print("\n"); +} + +// Recursively prints enums in nested types within descriptor, then +// prints enums contained at the top level in descriptor. +void Generator::PrintNestedEnums(const Descriptor& descriptor) const { + for (int i = 0; i < descriptor.nested_type_count(); ++i) { + PrintNestedEnums(*descriptor.nested_type(i)); + } + + for (int i = 0; i < descriptor.enum_type_count(); ++i) { + PrintEnum(*descriptor.enum_type(i)); + } +} + +void Generator::PrintTopLevelExtensions() const { + const bool is_extension = true; + for (int i = 0; i < file_->extension_count(); ++i) { + const FieldDescriptor& extension_field = *file_->extension(i); + printer_->Print("$name$ = ", "name", extension_field.name()); + PrintFieldDescriptor(extension_field, is_extension); + printer_->Print("\n"); + } + printer_->Print("\n"); +} + +// Prints Python equivalents of all Descriptors in |file|. +void Generator::PrintMessageDescriptors() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + PrintDescriptor(*file_->message_type(i)); + printer_->Print("\n"); + } +} + +void Generator::PrintServices() const { + for (int i = 0; i < file_->service_count(); ++i) { + PrintServiceDescriptor(*file_->service(i)); + PrintServiceClass(*file_->service(i)); + PrintServiceStub(*file_->service(i)); + printer_->Print("\n"); + } +} + +void Generator::PrintServiceDescriptor( + const ServiceDescriptor& descriptor) const { + printer_->Print("\n"); + string service_name = ModuleLevelServiceDescriptorName(descriptor); + string options_string; + descriptor.options().SerializeToString(&options_string); + + printer_->Print( + "$service_name$ = descriptor.ServiceDescriptor(\n", + "service_name", service_name); + printer_->Indent(); + map m; + m["name"] = descriptor.name(); + m["full_name"] = descriptor.full_name(); + m["index"] = SimpleItoa(descriptor.index()); + m["options_value"] = OptionsValue("ServiceOptions", options_string); + const char required_function_arguments[] = + "name='$name$',\n" + "full_name='$full_name$',\n" + "index=$index$,\n" + "options=$options_value$,\n" + "methods=[\n"; + printer_->Print(m, required_function_arguments); + for (int i = 0; i < descriptor.method_count(); ++i) { + const MethodDescriptor* method = descriptor.method(i); + string options_string; + method->options().SerializeToString(&options_string); + + m.clear(); + m["name"] = method->name(); + m["full_name"] = method->full_name(); + m["index"] = SimpleItoa(method->index()); + m["serialized_options"] = CEscape(options_string); + m["input_type"] = ModuleLevelDescriptorName(*(method->input_type())); + m["output_type"] = ModuleLevelDescriptorName(*(method->output_type())); + m["options_value"] = OptionsValue("MethodOptions", options_string); + printer_->Print("descriptor.MethodDescriptor(\n"); + printer_->Indent(); + printer_->Print( + m, + "name='$name$',\n" + "full_name='$full_name$',\n" + "index=$index$,\n" + "containing_service=None,\n" + "input_type=$input_type$,\n" + "output_type=$output_type$,\n" + "options=$options_value$,\n"); + printer_->Outdent(); + printer_->Print("),\n"); + } + + printer_->Outdent(); + printer_->Print("])\n\n"); +} + +void Generator::PrintServiceClass(const ServiceDescriptor& descriptor) const { + // Print the service. + printer_->Print("class $class_name$(service.Service):\n", + "class_name", descriptor.name()); + printer_->Indent(); + printer_->Print( + "__metaclass__ = service_reflection.GeneratedServiceType\n" + "$descriptor_key$ = $descriptor_name$\n", + "descriptor_key", kDescriptorKey, + "descriptor_name", ModuleLevelServiceDescriptorName(descriptor)); + printer_->Outdent(); +} + +void Generator::PrintServiceStub(const ServiceDescriptor& descriptor) const { + // Print the service stub. + printer_->Print("class $class_name$_Stub($class_name$):\n", + "class_name", descriptor.name()); + printer_->Indent(); + printer_->Print( + "__metaclass__ = service_reflection.GeneratedServiceStubType\n" + "$descriptor_key$ = $descriptor_name$\n", + "descriptor_key", kDescriptorKey, + "descriptor_name", ModuleLevelServiceDescriptorName(descriptor)); + printer_->Outdent(); +} + +// Prints statement assigning ModuleLevelDescriptorName(message_descriptor) +// to a Python Descriptor object for message_descriptor. +// +// Mutually recursive with PrintNestedDescriptors(). +void Generator::PrintDescriptor(const Descriptor& message_descriptor) const { + PrintNestedDescriptors(message_descriptor); + + printer_->Print("\n"); + printer_->Print("$descriptor_name$ = descriptor.Descriptor(\n", + "descriptor_name", + ModuleLevelDescriptorName(message_descriptor)); + printer_->Indent(); + map m; + m["name"] = message_descriptor.name(); + m["full_name"] = message_descriptor.full_name(); + m["filename"] = message_descriptor.file()->name(); + const char required_function_arguments[] = + "name='$name$',\n" + "full_name='$full_name$',\n" + "filename='$filename$',\n" + "containing_type=None,\n"; // TODO(robinson): Implement containing_type. + printer_->Print(m, required_function_arguments); + PrintFieldsInDescriptor(message_descriptor); + PrintExtensionsInDescriptor(message_descriptor); + // TODO(robinson): implement printing of nested_types. + printer_->Print("nested_types=[], # TODO(robinson): Implement.\n"); + printer_->Print("enum_types=[\n"); + printer_->Indent(); + for (int i = 0; i < message_descriptor.enum_type_count(); ++i) { + const string descriptor_name = ModuleLevelDescriptorName( + *message_descriptor.enum_type(i)); + printer_->Print(descriptor_name.c_str()); + printer_->Print(",\n"); + } + printer_->Outdent(); + printer_->Print("],\n"); + string options_string; + message_descriptor.options().SerializeToString(&options_string); + printer_->Print( + "options=$options_value$", + "options_value", OptionsValue("MessageOptions", options_string)); + printer_->Outdent(); + printer_->Print(")\n"); +} + +// Prints Python Descriptor objects for all nested types contained in +// message_descriptor. +// +// Mutually recursive with PrintDescriptor(). +void Generator::PrintNestedDescriptors( + const Descriptor& containing_descriptor) const { + for (int i = 0; i < containing_descriptor.nested_type_count(); ++i) { + PrintDescriptor(*containing_descriptor.nested_type(i)); + } +} + +// Prints all messages in |file|. +void Generator::PrintMessages() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + PrintMessage(*file_->message_type(i)); + printer_->Print("\n"); + } +} + +// Prints a Python class for the given message descriptor. We defer to the +// metaclass to do almost all of the work of actually creating a useful class. +// The purpose of this function and its many helper functions above is merely +// to output a Python version of the descriptors, which the metaclass in +// reflection.py will use to construct the meat of the class itself. +// +// Mutually recursive with PrintNestedMessages(). +void Generator::PrintMessage( + const Descriptor& message_descriptor) const { + printer_->Print("class $name$(message.Message):\n", "name", + message_descriptor.name()); + printer_->Indent(); + printer_->Print("__metaclass__ = reflection.GeneratedProtocolMessageType\n"); + PrintNestedMessages(message_descriptor); + map m; + m["descriptor_key"] = kDescriptorKey; + m["descriptor_name"] = ModuleLevelDescriptorName(message_descriptor); + printer_->Print(m, "$descriptor_key$ = $descriptor_name$\n"); + printer_->Outdent(); +} + +// Prints all nested messages within |containing_descriptor|. +// Mutually recursive with PrintMessage(). +void Generator::PrintNestedMessages( + const Descriptor& containing_descriptor) const { + for (int i = 0; i < containing_descriptor.nested_type_count(); ++i) { + printer_->Print("\n"); + PrintMessage(*containing_descriptor.nested_type(i)); + } +} + +// Recursively fixes foreign fields in all nested types in |descriptor|, then +// sets the message_type and enum_type of all message and enum fields to point +// to their respective descriptors. +void Generator::FixForeignFieldsInDescriptor( + const Descriptor& descriptor) const { + for (int i = 0; i < descriptor.nested_type_count(); ++i) { + FixForeignFieldsInDescriptor(*descriptor.nested_type(i)); + } + + for (int i = 0; i < descriptor.field_count(); ++i) { + const FieldDescriptor& field_descriptor = *descriptor.field(i); + FixForeignFieldsInField(&descriptor, field_descriptor, "fields_by_name"); + } +} + +// Sets any necessary message_type and enum_type attributes +// for the Python version of |field|. +// +// containing_type may be NULL, in which case this is a module-level field. +// +// python_dict_name is the name of the Python dict where we should +// look the field up in the containing type. (e.g., fields_by_name +// or extensions_by_name). We ignore python_dict_name if containing_type +// is NULL. +void Generator::FixForeignFieldsInField(const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const { + const string field_referencing_expression = FieldReferencingExpression( + containing_type, field, python_dict_name); + map m; + m["field_ref"] = field_referencing_expression; + const Descriptor* foreign_message_type = field.message_type(); + if (foreign_message_type) { + m["foreign_type"] = ModuleLevelDescriptorName(*foreign_message_type); + printer_->Print(m, "$field_ref$.message_type = $foreign_type$\n"); + } + const EnumDescriptor* enum_type = field.enum_type(); + if (enum_type) { + m["enum_type"] = ModuleLevelDescriptorName(*enum_type); + printer_->Print(m, "$field_ref$.enum_type = $enum_type$\n"); + } +} + +// Returns the module-level expression for the given FieldDescriptor. +// Only works for fields in the .proto file this Generator is generating for. +// +// containing_type may be NULL, in which case this is a module-level field. +// +// python_dict_name is the name of the Python dict where we should +// look the field up in the containing type. (e.g., fields_by_name +// or extensions_by_name). We ignore python_dict_name if containing_type +// is NULL. +string Generator::FieldReferencingExpression( + const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const { + // We should only ever be looking up fields in the current file. + // The only things we refer to from other files are message descriptors. + GOOGLE_CHECK_EQ(field.file(), file_) << field.file()->name() << " vs. " + << file_->name(); + if (!containing_type) { + return field.name(); + } + return strings::Substitute( + "$0.$1['$2']", + ModuleLevelDescriptorName(*containing_type), + python_dict_name, field.name()); +} + +// Prints statements setting the message_type and enum_type fields in the +// Python descriptor objects we've already output in ths file. We must +// do this in a separate step due to circular references (otherwise, we'd +// just set everything in the initial assignment statements). +void Generator::FixForeignFieldsInDescriptors() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + FixForeignFieldsInDescriptor(*file_->message_type(i)); + } + printer_->Print("\n"); +} + +// We need to not only set any necessary message_type fields, but +// also need to call RegisterExtension() on each message we're +// extending. +void Generator::FixForeignFieldsInExtensions() const { + // Top-level extensions. + for (int i = 0; i < file_->extension_count(); ++i) { + FixForeignFieldsInExtension(*file_->extension(i)); + } + // Nested extensions. + for (int i = 0; i < file_->message_type_count(); ++i) { + FixForeignFieldsInNestedExtensions(*file_->message_type(i)); + } +} + +void Generator::FixForeignFieldsInExtension( + const FieldDescriptor& extension_field) const { + GOOGLE_CHECK(extension_field.is_extension()); + // extension_scope() will be NULL for top-level extensions, which is + // exactly what FixForeignFieldsInField() wants. + FixForeignFieldsInField(extension_field.extension_scope(), extension_field, + "extensions_by_name"); + + map m; + // Confusingly, for FieldDescriptors that happen to be extensions, + // containing_type() means "extended type." + // On the other hand, extension_scope() will give us what we normally + // mean by containing_type(). + m["extended_message_class"] = ModuleLevelMessageName( + *extension_field.containing_type()); + m["field"] = FieldReferencingExpression(extension_field.extension_scope(), + extension_field, + "extensions_by_name"); + printer_->Print(m, "$extended_message_class$.RegisterExtension($field$)\n"); +} + +void Generator::FixForeignFieldsInNestedExtensions( + const Descriptor& descriptor) const { + // Recursively fix up extensions in all nested types. + for (int i = 0; i < descriptor.nested_type_count(); ++i) { + FixForeignFieldsInNestedExtensions(*descriptor.nested_type(i)); + } + // Fix up extensions directly contained within this type. + for (int i = 0; i < descriptor.extension_count(); ++i) { + FixForeignFieldsInExtension(*descriptor.extension(i)); + } +} + +// Returns a Python expression that instantiates a Python EnumValueDescriptor +// object for the given C++ descriptor. +void Generator::PrintEnumValueDescriptor( + const EnumValueDescriptor& descriptor) const { + // TODO(robinson): Fix up EnumValueDescriptor "type" fields. + // More circular references. ::sigh:: + string options_string; + descriptor.options().SerializeToString(&options_string); + map m; + m["name"] = descriptor.name(); + m["index"] = SimpleItoa(descriptor.index()); + m["number"] = SimpleItoa(descriptor.number()); + m["options"] = OptionsValue("EnumValueOptions", options_string); + printer_->Print( + m, + "descriptor.EnumValueDescriptor(\n" + " name='$name$', index=$index$, number=$number$,\n" + " options=$options$,\n" + " type=None)"); +} + +string Generator::OptionsValue( + const string& class_name, const string& serialized_options) const { + if (serialized_options.length() == 0 || GeneratingDescriptorProto()) { + return "None"; + } else { + string full_class_name = "descriptor_pb2." + class_name; + return "descriptor._ParseOptions(" + full_class_name + "(), '" + + CEscape(serialized_options)+ "')"; + } +} + +// Prints an expression for a Python FieldDescriptor for |field|. +void Generator::PrintFieldDescriptor( + const FieldDescriptor& field, bool is_extension) const { + string options_string; + field.options().SerializeToString(&options_string); + map m; + m["name"] = field.name(); + m["full_name"] = field.full_name(); + m["index"] = SimpleItoa(field.index()); + m["number"] = SimpleItoa(field.number()); + m["type"] = SimpleItoa(field.type()); + m["cpp_type"] = SimpleItoa(field.cpp_type()); + m["label"] = SimpleItoa(field.label()); + m["default_value"] = StringifyDefaultValue(field); + m["is_extension"] = is_extension ? "True" : "False"; + m["options"] = OptionsValue("FieldOptions", options_string); + // We always set message_type and enum_type to None at this point, and then + // these fields in correctly after all referenced descriptors have been + // defined and/or imported (see FixForeignFieldsInDescriptors()). + const char field_descriptor_decl[] = + "descriptor.FieldDescriptor(\n" + " name='$name$', full_name='$full_name$', index=$index$,\n" + " number=$number$, type=$type$, cpp_type=$cpp_type$, label=$label$,\n" + " default_value=$default_value$,\n" + " message_type=None, enum_type=None, containing_type=None,\n" + " is_extension=$is_extension$, extension_scope=None,\n" + " options=$options$)"; + printer_->Print(m, field_descriptor_decl); +} + +// Helper for Print{Fields,Extensions}InDescriptor(). +void Generator::PrintFieldDescriptorsInDescriptor( + const Descriptor& message_descriptor, + bool is_extension, + const string& list_variable_name, + int (Descriptor::*CountFn)() const, + const FieldDescriptor* (Descriptor::*GetterFn)(int) const) const { + printer_->Print("$list$=[\n", "list", list_variable_name); + printer_->Indent(); + for (int i = 0; i < (message_descriptor.*CountFn)(); ++i) { + PrintFieldDescriptor(*(message_descriptor.*GetterFn)(i), + is_extension); + printer_->Print(",\n"); + } + printer_->Outdent(); + printer_->Print("],\n"); +} + +// Prints a statement assigning "fields" to a list of Python FieldDescriptors, +// one for each field present in message_descriptor. +void Generator::PrintFieldsInDescriptor( + const Descriptor& message_descriptor) const { + const bool is_extension = false; + PrintFieldDescriptorsInDescriptor( + message_descriptor, is_extension, "fields", + &Descriptor::field_count, &Descriptor::field); +} + +// Prints a statement assigning "extensions" to a list of Python +// FieldDescriptors, one for each extension present in message_descriptor. +void Generator::PrintExtensionsInDescriptor( + const Descriptor& message_descriptor) const { + const bool is_extension = true; + PrintFieldDescriptorsInDescriptor( + message_descriptor, is_extension, "extensions", + &Descriptor::extension_count, &Descriptor::extension); +} + +bool Generator::GeneratingDescriptorProto() const { + return file_->name() == "google/protobuf/descriptor.proto"; +} + +// Returns the unique Python module-level identifier given to a descriptor. +// This name is module-qualified iff the given descriptor describes an +// entity that doesn't come from the current file. +template +string Generator::ModuleLevelDescriptorName( + const DescriptorT& descriptor) const { + // FIXME(robinson): + // We currently don't worry about collisions with underscores in the type + // names, so these would collide in nasty ways if found in the same file: + // OuterProto.ProtoA.ProtoB + // OuterProto_ProtoA.ProtoB # Underscore instead of period. + // As would these: + // OuterProto.ProtoA_.ProtoB + // OuterProto.ProtoA._ProtoB # Leading vs. trailing underscore. + // (Contrived, but certainly possible). + // + // The C++ implementation doesn't guard against this either. Leaving + // it for now... + string name = NamePrefixedWithNestedTypes(descriptor, "_"); + UpperString(&name); + // Module-private for now. Easy to make public later; almost impossible + // to make private later. + name = "_" + name; + // We now have the name relative to its own module. Also qualify with + // the module name iff this descriptor is from a different .proto file. + if (descriptor.file() != file_) { + name = ModuleName(descriptor.file()->name()) + "." + name; + } + return name; +} + +// Returns the name of the message class itself, not the descriptor. +// Like ModuleLevelDescriptorName(), module-qualifies the name iff +// the given descriptor describes an entity that doesn't come from +// the current file. +string Generator::ModuleLevelMessageName(const Descriptor& descriptor) const { + string name = NamePrefixedWithNestedTypes(descriptor, "."); + if (descriptor.file() != file_) { + name = ModuleName(descriptor.file()->name()) + "." + name; + } + return name; +} + +// Returns the unique Python module-level identifier given to a service +// descriptor. +string Generator::ModuleLevelServiceDescriptorName( + const ServiceDescriptor& descriptor) const { + string name = descriptor.name(); + UpperString(&name); + name = "_" + name; + if (descriptor.file() != file_) { + name = ModuleName(descriptor.file()->name()) + "." + name; + } + return name; +} + +} // namespace python +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h new file mode 100644 index 00000000..98ee0c6d --- /dev/null +++ b/src/google/protobuf/compiler/python/python_generator.h @@ -0,0 +1,129 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: robinson@google.com (Will Robinson) +// +// Generates Python code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_PYTHON_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_PYTHON_GENERATOR_H__ + +#include + +#include +#include + +namespace google { +namespace protobuf { + +class Descriptor; +class EnumDescriptor; +class EnumValueDescriptor; +class FieldDescriptor; +class ServiceDescriptor; + +namespace io { class Printer; } + +namespace compiler { +namespace python { + +// CodeGenerator implementation for generated Python protocol buffer classes. +// If you create your own protocol compiler binary and you want it to support +// Python output, you can do so by registering an instance of this +// CodeGenerator with the CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT Generator : public CodeGenerator { + public: + Generator(); + virtual ~Generator(); + + // CodeGenerator methods. + virtual bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + void PrintImports() const; + void PrintTopLevelEnums() const; + void PrintAllNestedEnumsInFile() const; + void PrintNestedEnums(const Descriptor& descriptor) const; + void PrintEnum(const EnumDescriptor& enum_descriptor) const; + + void PrintTopLevelExtensions() const; + + void PrintFieldDescriptor( + const FieldDescriptor& field, bool is_extension) const; + void PrintFieldDescriptorsInDescriptor( + const Descriptor& message_descriptor, + bool is_extension, + const string& list_variable_name, + int (Descriptor::*CountFn)() const, + const FieldDescriptor* (Descriptor::*GetterFn)(int) const) const; + void PrintFieldsInDescriptor(const Descriptor& message_descriptor) const; + void PrintExtensionsInDescriptor(const Descriptor& message_descriptor) const; + void PrintMessageDescriptors() const; + void PrintDescriptor(const Descriptor& message_descriptor) const; + void PrintNestedDescriptors(const Descriptor& containing_descriptor) const; + + void PrintMessages() const; + void PrintMessage(const Descriptor& message_descriptor) const; + void PrintNestedMessages(const Descriptor& containing_descriptor) const; + + void FixForeignFieldsInDescriptors() const; + void FixForeignFieldsInDescriptor(const Descriptor& descriptor) const; + void FixForeignFieldsInField(const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const; + string FieldReferencingExpression(const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const; + + void FixForeignFieldsInExtensions() const; + void FixForeignFieldsInExtension( + const FieldDescriptor& extension_field) const; + void FixForeignFieldsInNestedExtensions(const Descriptor& descriptor) const; + + void PrintServices() const; + void PrintServiceDescriptor(const ServiceDescriptor& descriptor) const; + void PrintServiceClass(const ServiceDescriptor& descriptor) const; + void PrintServiceStub(const ServiceDescriptor& descriptor) const; + + void PrintEnumValueDescriptor(const EnumValueDescriptor& descriptor) const; + string OptionsValue(const string& class_name, + const string& serialized_options) const; + bool GeneratingDescriptorProto() const; + + template + string ModuleLevelDescriptorName(const DescriptorT& descriptor) const; + string ModuleLevelMessageName(const Descriptor& descriptor) const; + string ModuleLevelServiceDescriptorName( + const ServiceDescriptor& descriptor) const; + + // Very coarse-grained lock to ensure that Generate() is reentrant. + // Guards file_ and printer_. + mutable Mutex mutex_; + mutable const FileDescriptor* file_; // Set in Generate(). Under mutex_. + mutable io::Printer* printer_; // Set in Generate(). Under mutex_. + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator); +}; + +} // namespace python +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_PYTHON_GENERATOR_H__ diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc new file mode 100644 index 00000000..c75cf623 --- /dev/null +++ b/src/google/protobuf/descriptor.cc @@ -0,0 +1,2838 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef PACKAGE // autoheader #defines this. :( + +namespace google { +namespace protobuf { + +const FieldDescriptor::CppType +FieldDescriptor::kTypeToCppTypeMap[MAX_TYPE + 1] = { + static_cast(0), // 0 is reserved for errors + + CPPTYPE_DOUBLE, // TYPE_DOUBLE + CPPTYPE_FLOAT, // TYPE_FLOAT + CPPTYPE_INT64, // TYPE_INT64 + CPPTYPE_UINT64, // TYPE_UINT64 + CPPTYPE_INT32, // TYPE_INT32 + CPPTYPE_UINT64, // TYPE_FIXED64 + CPPTYPE_UINT32, // TYPE_FIXED32 + CPPTYPE_BOOL, // TYPE_BOOL + CPPTYPE_STRING, // TYPE_STRING + CPPTYPE_MESSAGE, // TYPE_GROUP + CPPTYPE_MESSAGE, // TYPE_MESSAGE + CPPTYPE_STRING, // TYPE_BYTES + CPPTYPE_UINT32, // TYPE_UINT32 + CPPTYPE_ENUM, // TYPE_ENUM + CPPTYPE_INT32, // TYPE_SFIXED32 + CPPTYPE_INT64, // TYPE_SFIXED64 + CPPTYPE_INT32, // TYPE_SINT32 + CPPTYPE_INT64, // TYPE_SINT64 +}; + +const char * const FieldDescriptor::kTypeToName[MAX_TYPE + 1] = { + "ERROR", // 0 is reserved for errors + + "double", // TYPE_DOUBLE + "float", // TYPE_FLOAT + "int64", // TYPE_INT64 + "uint64", // TYPE_UINT64 + "int32", // TYPE_INT32 + "fixed64", // TYPE_FIXED64 + "fixed32", // TYPE_FIXED32 + "bool", // TYPE_BOOL + "string", // TYPE_STRING + "group", // TYPE_GROUP + "message", // TYPE_MESSAGE + "bytes", // TYPE_BYTES + "uint32", // TYPE_UINT32 + "enum", // TYPE_ENUM + "sfixed32", // TYPE_SFIXED32 + "sfixed64", // TYPE_SFIXED64 + "sint32", // TYPE_SINT32 + "sint64", // TYPE_SINT64 +}; + +const char * const FieldDescriptor::kLabelToName[MAX_LABEL + 1] = { + "ERROR", // 0 is reserved for errors + + "optional", // LABEL_OPTIONAL + "required", // LABEL_REQUIRED + "repeated", // LABEL_REPEATED +}; + +#ifndef _MSC_VER // MSVC doesn't need these and won't even accept them. +const int FieldDescriptor::kMaxNumber; +const int FieldDescriptor::kFirstReservedNumber; +const int FieldDescriptor::kLastReservedNumber; +#endif + +namespace { + +const string kEmptyString; + +// A DescriptorPool contains a bunch of hash_maps to implement the +// various Find*By*() methods. Since hashtable lookups are O(1), it's +// most efficient to construct a fixed set of large hash_maps used by +// all objects in the pool rather than construct one or more small +// hash_maps for each object. +// +// The keys to these hash_maps are (parent, name) or (parent, number) +// pairs. Unfortunately STL doesn't provide hash functions for pair<>, +// so we must invent our own. +// +// TODO(kenton): Use StringPiece rather than const char* in keys? It would +// be a lot cleaner but we'd just have to convert it back to const char* +// for the open source release. + +typedef pair PointerStringPair; + +// Used by GCC/SGI STL only. (Why isn't this provided by the standard +// library? :( ) +struct CStringEqual { + inline bool operator()(const char* a, const char* b) const { + return strcmp(a, b) == 0; + } +}; + +struct PointerStringPairEqual { + inline bool operator()(const PointerStringPair& a, + const PointerStringPair& b) const { + return a.first == b.first && strcmp(a.second, b.second) == 0; + } +}; + +template +struct PointerIntegerPairHash { + size_t operator()(const PairType& p) const { + // FIXME(kenton): What is the best way to compute this hash? I have + // no idea! This seems a bit better than an XOR. + return reinterpret_cast(p.first) * ((1 << 16) - 1) + p.second; + } + + // Used only by MSVC and platforms where hash_map is not available. + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + inline bool operator()(const PairType& a, const PairType& b) const { + return a.first < b.first || + (a.first == b.first && a.second < b.second); + } +}; + +typedef pair DescriptorIntPair; +typedef pair EnumIntPair; + +struct PointerStringPairHash { + size_t operator()(const PointerStringPair& p) const { + // FIXME(kenton): What is the best way to compute this hash? I have + // no idea! This seems a bit better than an XOR. + hash cstring_hash; + return reinterpret_cast(p.first) * ((1 << 16) - 1) + + cstring_hash(p.second); + } + + // Used only by MSVC and platforms where hash_map is not available. + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + inline bool operator()(const PointerStringPair& a, + const PointerStringPair& b) const { + if (a.first < b.first) return true; + if (a.first > b.first) return false; + return strcmp(a.second, b.second) < 0; + } +}; + + +struct Symbol { + enum Type { + NULL_SYMBOL, MESSAGE, FIELD, ENUM, ENUM_VALUE, SERVICE, METHOD, PACKAGE + }; + Type type; + union { + const Descriptor* descriptor; + const FieldDescriptor* field_descriptor; + const EnumDescriptor* enum_descriptor; + const EnumValueDescriptor* enum_value_descriptor; + const ServiceDescriptor* service_descriptor; + const MethodDescriptor* method_descriptor; + const FileDescriptor* package_file_descriptor; + }; + + inline Symbol() : type(NULL_SYMBOL) { descriptor = NULL; } + inline bool IsNull() const { return type == NULL_SYMBOL; } + +#define CONSTRUCTOR(TYPE, TYPE_CONSTANT, FIELD) \ + inline explicit Symbol(const TYPE* value) { \ + type = TYPE_CONSTANT; \ + this->FIELD = value; \ + } + + CONSTRUCTOR(Descriptor , MESSAGE , descriptor ) + CONSTRUCTOR(FieldDescriptor , FIELD , field_descriptor ) + CONSTRUCTOR(EnumDescriptor , ENUM , enum_descriptor ) + CONSTRUCTOR(EnumValueDescriptor, ENUM_VALUE, enum_value_descriptor ) + CONSTRUCTOR(ServiceDescriptor , SERVICE , service_descriptor ) + CONSTRUCTOR(MethodDescriptor , METHOD , method_descriptor ) + CONSTRUCTOR(FileDescriptor , PACKAGE , package_file_descriptor) +#undef CONSTRUCTOR + + const FileDescriptor* GetFile() const { + switch (type) { + case NULL_SYMBOL: return NULL; + case MESSAGE : return descriptor ->file(); + case FIELD : return field_descriptor ->file(); + case ENUM : return enum_descriptor ->file(); + case ENUM_VALUE : return enum_value_descriptor->type()->file(); + case SERVICE : return service_descriptor ->file(); + case METHOD : return method_descriptor ->service()->file(); + case PACKAGE : return package_file_descriptor; + } + return NULL; + } +}; + +const Symbol kNullSymbol; + +typedef hash_map, CStringEqual> + SymbolsByNameMap; +typedef hash_map + SymbolsByParentMap; +typedef hash_map, CStringEqual> + FilesByNameMap; +typedef hash_map > + FieldsByNumberMap; +typedef hash_map > + EnumValuesByNumberMap; + +} // anonymous namespace + +// =================================================================== +// DescriptorPool::Tables + +class DescriptorPool::Tables { + public: + Tables(); + ~Tables(); + + // Checkpoint the state of the tables. Future calls to Rollback() will + // return the Tables to this state. This is used when building files, since + // some kinds of validation errors cannot be detected until the file's + // descriptors have already been added to the tables. BuildFile() calls + // Checkpoint() before it starts building and Rollback() if it encounters + // an error. + void Checkpoint(); + + // Roll back the Tables to the state of the last Checkpoint(), removing + // everything that was added after that point. + void Rollback(); + + // The stack of files which are currently being built. Used to detect + // cyclic dependencies when loading files from a DescriptorDatabase. Not + // used when fallback_database_ == NULL. + vector pending_files_; + + // A set of files which we have tried to load from the fallback database + // and encountered errors. We will not attempt to load them again. + // Not used when fallback_database_ == NULL. + hash_set known_bad_files_; + + // ----------------------------------------------------------------- + // Finding items. + + // Find symbols. These return a null Symbol (symbol.IsNull() is true) + // if not found. FindSymbolOfType() additionally returns null if the + // symbol is not of the given type. + inline Symbol FindSymbol(const string& key) const; + inline Symbol FindSymbolOfType(const string& key, + const Symbol::Type type) const; + inline Symbol FindNestedSymbol(const void* parent, + const string& name) const; + inline Symbol FindNestedSymbolOfType(const void* parent, + const string& name, + const Symbol::Type type) const; + + // These return NULL if not found. + inline const FileDescriptor* FindFile(const string& key) const; + inline const FieldDescriptor* FindFieldByNumber( + const Descriptor* parent, int number) const; + inline const EnumValueDescriptor* FindEnumValueByNumber( + const EnumDescriptor* parent, int number) const; + + // ----------------------------------------------------------------- + // Adding items. + + // These add items to the corresponding tables. They return false if + // the key already exists in the table. For AddSymbol(), the strings passed + // in must be ones that were constructed using AllocateString(), as they will + // be used as keys in the symbols_by_name_ and symbols_by_parent_ maps + // without copying. (If parent is NULL, nothing is added to + // symbols_by_parent_.) + bool AddSymbol(const string& full_name, + const void* parent, const string& name, + Symbol symbol); + bool AddFile(const FileDescriptor* file); + bool AddFieldByNumber(const FieldDescriptor* field); + bool AddEnumValueByNumber(const EnumValueDescriptor* value); + + // Like AddSymbol(), but only adds to symbols_by_parent_, not + // symbols_by_name_. Used for enum values, which need to be registered + // under multiple parents (their type and its parent). + bool AddAliasUnderParent(const void* parent, const string& name, + Symbol symbol); + + // ----------------------------------------------------------------- + // Allocating memory. + + // Allocate an object which will be reclaimed when the pool is + // destroyed. Note that the object's destructor will never be called, + // so its fields must be plain old data (primitive data types and + // pointers). All of the descriptor types are such objects. + template Type* Allocate(); + + // Allocate an array of objects which will be reclaimed when the + // pool in destroyed. Again, destructors are never called. + template Type* AllocateArray(int count); + + // Allocate a string which will be destroyed when the pool is destroyed. + // The string is initialized to the given value for convenience. + string* AllocateString(const string& value); + + // Allocate a protocol message object. + template Type* AllocateMessage(); + + private: + vector strings_; // All strings in the pool. + vector messages_; // All messages in the pool. + vector allocations_; // All other memory allocated in the pool. + + SymbolsByNameMap symbols_by_name_; + SymbolsByParentMap symbols_by_parent_; + FilesByNameMap files_by_name_; + FieldsByNumberMap fields_by_number_; // Includes extensions. + EnumValuesByNumberMap enum_values_by_number_; + + int strings_before_checkpoint_; + int messages_before_checkpoint_; + int allocations_before_checkpoint_; + vector symbols_after_checkpoint_; + vector symbols_by_parent_after_checkpoint_; + vector files_after_checkpoint_; + vector field_numbers_after_checkpoint_; + vector enum_numbers_after_checkpoint_; + + // Allocate some bytes which will be reclaimed when the pool is + // destroyed. + void* AllocateBytes(int size); +}; + +DescriptorPool::Tables::Tables() + : strings_before_checkpoint_(0), + messages_before_checkpoint_(0), + allocations_before_checkpoint_(0) {} + +DescriptorPool::Tables::~Tables() { + for (int i = 0; i < allocations_.size(); i++) { + operator delete(allocations_[i]); + } + STLDeleteElements(&strings_); + STLDeleteElements(&messages_); +} + +void DescriptorPool::Tables::Checkpoint() { + strings_before_checkpoint_ = strings_.size(); + messages_before_checkpoint_ = messages_.size(); + allocations_before_checkpoint_ = allocations_.size(); + + symbols_after_checkpoint_.clear(); + symbols_by_parent_after_checkpoint_.clear(); + files_after_checkpoint_.clear(); + field_numbers_after_checkpoint_.clear(); + enum_numbers_after_checkpoint_.clear(); +} + +void DescriptorPool::Tables::Rollback() { + for (int i = 0; i < symbols_after_checkpoint_.size(); i++) { + symbols_by_name_.erase(symbols_after_checkpoint_[i]); + } + for (int i = 0; i < symbols_by_parent_after_checkpoint_.size(); i++) { + symbols_by_parent_.erase(symbols_by_parent_after_checkpoint_[i]); + } + for (int i = 0; i < files_after_checkpoint_.size(); i++) { + files_by_name_.erase(files_after_checkpoint_[i]); + } + for (int i = 0; i < field_numbers_after_checkpoint_.size(); i++) { + fields_by_number_.erase(field_numbers_after_checkpoint_[i]); + } + for (int i = 0; i < enum_numbers_after_checkpoint_.size(); i++) { + enum_values_by_number_.erase(enum_numbers_after_checkpoint_[i]); + } + + symbols_after_checkpoint_.clear(); + symbols_by_parent_after_checkpoint_.clear(); + files_after_checkpoint_.clear(); + field_numbers_after_checkpoint_.clear(); + enum_numbers_after_checkpoint_.clear(); + + STLDeleteContainerPointers( + strings_.begin() + strings_before_checkpoint_, strings_.end()); + STLDeleteContainerPointers( + messages_.begin() + messages_before_checkpoint_, messages_.end()); + for (int i = allocations_before_checkpoint_; i < allocations_.size(); i++) { + operator delete(allocations_[i]); + } + + strings_.resize(strings_before_checkpoint_); + messages_.resize(messages_before_checkpoint_); + allocations_.resize(allocations_before_checkpoint_); +} + +// ------------------------------------------------------------------- + +inline Symbol DescriptorPool::Tables::FindSymbol(const string& key) const { + const Symbol* result = FindOrNull(symbols_by_name_, key.c_str()); + if (result == NULL) { + return kNullSymbol; + } else { + return *result; + } +} + +inline Symbol DescriptorPool::Tables::FindSymbolOfType( + const string& key, const Symbol::Type type) const { + Symbol result = FindSymbol(key); + if (result.type != type) return kNullSymbol; + return result; +} + +inline Symbol DescriptorPool::Tables::FindNestedSymbol( + const void* parent, const string& name) const { + const Symbol* result = + FindOrNull(symbols_by_parent_, PointerStringPair(parent, name.c_str())); + if (result == NULL) { + return kNullSymbol; + } else { + return *result; + } +} + +inline Symbol DescriptorPool::Tables::FindNestedSymbolOfType( + const void* parent, const string& name, const Symbol::Type type) const { + Symbol result = FindNestedSymbol(parent, name); + if (result.type != type) return kNullSymbol; + return result; +} + +inline const FileDescriptor* DescriptorPool::Tables::FindFile( + const string& key) const { + return FindPtrOrNull(files_by_name_, key.c_str()); +} + +inline const FieldDescriptor* DescriptorPool::Tables::FindFieldByNumber( + const Descriptor* parent, int number) const { + return FindPtrOrNull(fields_by_number_, make_pair(parent, number)); +} + +inline const EnumValueDescriptor* DescriptorPool::Tables::FindEnumValueByNumber( + const EnumDescriptor* parent, int number) const { + return FindPtrOrNull(enum_values_by_number_, make_pair(parent, number)); +} + +// ------------------------------------------------------------------- + +bool DescriptorPool::Tables::AddSymbol( + const string& full_name, + const void* parent, const string& name, + Symbol symbol) { + if (InsertIfNotPresent(&symbols_by_name_, full_name.c_str(), symbol)) { + symbols_after_checkpoint_.push_back(full_name.c_str()); + + if (parent != NULL && !AddAliasUnderParent(parent, name, symbol)) { + GOOGLE_LOG(DFATAL) << "\"" << full_name << "\" not previously defined in " + "symbols_by_name_, but was defined in symbols_by_parent_; " + "this shouldn't be possible."; + return false; + } + + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddAliasUnderParent( + const void* parent, const string& name, Symbol symbol) { + PointerStringPair by_parent_key(parent, name.c_str()); + if (InsertIfNotPresent(&symbols_by_parent_, by_parent_key, symbol)) { + symbols_by_parent_after_checkpoint_.push_back(by_parent_key); + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddFile(const FileDescriptor* file) { + if (InsertIfNotPresent(&files_by_name_, file->name().c_str(), file)) { + files_after_checkpoint_.push_back(file->name().c_str()); + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddFieldByNumber(const FieldDescriptor* field) { + DescriptorIntPair key(field->containing_type(), field->number()); + if (InsertIfNotPresent(&fields_by_number_, key, field)) { + field_numbers_after_checkpoint_.push_back(key); + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddEnumValueByNumber( + const EnumValueDescriptor* value) { + EnumIntPair key(value->type(), value->number()); + if (InsertIfNotPresent(&enum_values_by_number_, key, value)) { + enum_numbers_after_checkpoint_.push_back(key); + return true; + } else { + return false; + } +} + +// ------------------------------------------------------------------- + +template +Type* DescriptorPool::Tables::Allocate() { + return reinterpret_cast(AllocateBytes(sizeof(Type))); +} + +template +Type* DescriptorPool::Tables::AllocateArray(int count) { + return reinterpret_cast(AllocateBytes(sizeof(Type) * count)); +} + +string* DescriptorPool::Tables::AllocateString(const string& value) { + string* result = new string(value); + strings_.push_back(result); + return result; +} + +template +Type* DescriptorPool::Tables::AllocateMessage() { + Type* result = new Type; + messages_.push_back(result); + return result; +} + +void* DescriptorPool::Tables::AllocateBytes(int size) { + // TODO(kenton): Would it be worthwhile to implement this in some more + // sophisticated way? Probably not for the open source release, but for + // internal use we could easily plug in one of our existing memory pool + // allocators... + if (size == 0) return NULL; + + void* result = operator new(size); + allocations_.push_back(result); + return result; +} + +// =================================================================== +// DescriptorPool + +DescriptorPool::ErrorCollector::~ErrorCollector() {} + +DescriptorPool::DescriptorPool() + : mutex_(NULL), + fallback_database_(NULL), + default_error_collector_(NULL), + underlay_(NULL), + tables_(new Tables), + enforce_dependencies_(true), + last_internal_build_generated_file_call_(NULL) {} + +DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, + ErrorCollector* error_collector) + : mutex_(new Mutex), + fallback_database_(fallback_database), + default_error_collector_(error_collector), + underlay_(NULL), + tables_(new Tables), + enforce_dependencies_(true), + last_internal_build_generated_file_call_(NULL) { +} + +DescriptorPool::DescriptorPool(const DescriptorPool* underlay) + : mutex_(NULL), + fallback_database_(NULL), + default_error_collector_(NULL), + underlay_(underlay), + tables_(new Tables), + last_internal_build_generated_file_call_(NULL) {} + +DescriptorPool::~DescriptorPool() { + if (mutex_ != NULL) delete mutex_; +} + +// DescriptorPool::BuildFile() defined later. +// DescriptorPool::BuildFileCollectingErrors() defined later. +// DescriptorPool::InternalBuildGeneratedFile() defined later. + +const DescriptorPool* DescriptorPool::generated_pool() { + return internal_generated_pool(); +} + +DescriptorPool* DescriptorPool::internal_generated_pool() { + static DescriptorPool singleton; + return &singleton; +} + +void DescriptorPool::InternalDontEnforceDependencies() { + enforce_dependencies_ = false; +} + +// Find*By* methods ================================================== + +// TODO(kenton): There's a lot of repeated code here, but I'm not sure if +// there's any good way to factor it out. Think about this some time when +// there's nothing more important to do (read: never). + +const FileDescriptor* DescriptorPool::FindFileByName(const string& name) const { + MutexLockMaybe lock(mutex_); + const FileDescriptor* result = tables_->FindFile(name); + if (result != NULL) return result; + if (underlay_ != NULL) { + const FileDescriptor* result = underlay_->FindFileByName(name); + if (result != NULL) return result; + } + if (TryFindFileInFallbackDatabase(name)) { + const FileDescriptor* result = tables_->FindFile(name); + if (result != NULL) return result; + } + return NULL; +} + +const FileDescriptor* DescriptorPool::FindFileContainingSymbol( + const string& symbol_name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbol(symbol_name); + if (!result.IsNull()) return result.GetFile(); + if (underlay_ != NULL) { + const FileDescriptor* result = + underlay_->FindFileContainingSymbol(symbol_name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(symbol_name)) { + Symbol result = tables_->FindSymbol(symbol_name); + if (!result.IsNull()) return result.GetFile(); + } + return NULL; +} + +const Descriptor* DescriptorPool::FindMessageTypeByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::MESSAGE); + if (!result.IsNull()) return result.descriptor; + if (underlay_ != NULL) { + const Descriptor* result = underlay_->FindMessageTypeByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::MESSAGE); + if (!result.IsNull()) return result.descriptor; + } + return NULL; +} + +const FieldDescriptor* DescriptorPool::FindFieldByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && !result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + if (underlay_ != NULL) { + const FieldDescriptor* result = underlay_->FindFieldByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && !result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + } + return NULL; +} + +const FieldDescriptor* DescriptorPool::FindExtensionByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + if (underlay_ != NULL) { + const FieldDescriptor* result = underlay_->FindExtensionByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + } + return NULL; +} + +const EnumDescriptor* DescriptorPool::FindEnumTypeByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM); + if (!result.IsNull()) return result.enum_descriptor; + if (underlay_ != NULL) { + const EnumDescriptor* result = underlay_->FindEnumTypeByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM); + if (!result.IsNull()) return result.enum_descriptor; + } + return NULL; +} + +const EnumValueDescriptor* DescriptorPool::FindEnumValueByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM_VALUE); + if (!result.IsNull()) return result.enum_value_descriptor; + if (underlay_ != NULL) { + const EnumValueDescriptor* result = underlay_->FindEnumValueByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM_VALUE); + if (!result.IsNull()) return result.enum_value_descriptor; + } + return NULL; +} + +const ServiceDescriptor* DescriptorPool::FindServiceByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::SERVICE); + if (!result.IsNull()) return result.service_descriptor; + if (underlay_ != NULL) { + const ServiceDescriptor* result = underlay_->FindServiceByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::SERVICE); + if (!result.IsNull()) return result.service_descriptor; + } + return NULL; +} + +const MethodDescriptor* DescriptorPool::FindMethodByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::METHOD); + if (!result.IsNull()) return result.method_descriptor; + if (underlay_ != NULL) { + const MethodDescriptor* result = underlay_->FindMethodByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::METHOD); + if (!result.IsNull()) return result.method_descriptor; + } + return NULL; +} + +const FieldDescriptor* DescriptorPool::FindExtensionByNumber( + const Descriptor* extendee, int number) const { + MutexLockMaybe lock(mutex_); + const FieldDescriptor* result = tables_->FindFieldByNumber(extendee, number); + if (result != NULL && result->is_extension()) { + return result; + } + if (underlay_ != NULL) { + const FieldDescriptor* result = + underlay_->FindExtensionByNumber(extendee, number); + if (result != NULL) return result; + } + if (TryFindExtensionInFallbackDatabase(extendee, number)) { + const FieldDescriptor* result = + tables_->FindFieldByNumber(extendee, number); + if (result != NULL && result->is_extension()) { + return result; + } + } + return NULL; +} + +// ------------------------------------------------------------------- + +const FieldDescriptor* +Descriptor::FindFieldByNumber(int key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + const FieldDescriptor* result = + file()->pool()->tables_->FindFieldByNumber(this, key); + if (result == NULL || result->is_extension()) { + return NULL; + } else { + return result; + } +} + +const FieldDescriptor* +Descriptor::FindFieldByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::FIELD); + if (!result.IsNull() && !result.field_descriptor->is_extension()) { + return result.field_descriptor; + } else { + return NULL; + } +} + +const FieldDescriptor* +Descriptor::FindExtensionByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } else { + return NULL; + } +} + +const Descriptor* +Descriptor::FindNestedTypeByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::MESSAGE); + if (!result.IsNull()) { + return result.descriptor; + } else { + return NULL; + } +} + +const EnumDescriptor* +Descriptor::FindEnumTypeByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::ENUM); + if (!result.IsNull()) { + return result.enum_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +Descriptor::FindEnumValueByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType( + this, key, Symbol::ENUM_VALUE); + if (!result.IsNull()) { + return result.enum_value_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +EnumDescriptor::FindValueByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType( + this, key, Symbol::ENUM_VALUE); + if (!result.IsNull()) { + return result.enum_value_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +EnumDescriptor::FindValueByNumber(int key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + return file()->pool()->tables_->FindEnumValueByNumber(this, key); +} + +const MethodDescriptor* +ServiceDescriptor::FindMethodByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::METHOD); + if (!result.IsNull()) { + return result.method_descriptor; + } else { + return NULL; + } +} + +const Descriptor* +FileDescriptor::FindMessageTypeByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::MESSAGE); + if (!result.IsNull()) { + return result.descriptor; + } else { + return NULL; + } +} + +const EnumDescriptor* +FileDescriptor::FindEnumTypeByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::ENUM); + if (!result.IsNull()) { + return result.enum_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +FileDescriptor::FindEnumValueByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::ENUM_VALUE); + if (!result.IsNull()) { + return result.enum_value_descriptor; + } else { + return NULL; + } +} + +const ServiceDescriptor* +FileDescriptor::FindServiceByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::SERVICE); + if (!result.IsNull()) { + return result.service_descriptor; + } else { + return NULL; + } +} + +const FieldDescriptor* +FileDescriptor::FindExtensionByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } else { + return NULL; + } +} + +bool Descriptor::IsExtensionNumber(int number) const { + // Linear search should be fine because we don't expect a message to have + // more than a couple extension ranges. + for (int i = 0; i < extension_range_count(); i++) { + if (number >= extension_range(i)->start && + number < extension_range(i)->end) { + return true; + } + } + return false; +} + +// ------------------------------------------------------------------- + +bool DescriptorPool::TryFindFileInFallbackDatabase(const string& name) const { + if (fallback_database_ == NULL) return false; + + if (tables_->known_bad_files_.count(name) > 0) return false; + + FileDescriptorProto file_proto; + if (!fallback_database_->FindFileByName(name, &file_proto) || + BuildFileFromDatabase(file_proto) == NULL) { + tables_->known_bad_files_.insert(name); + return false; + } + + return true; +} + +bool DescriptorPool::TryFindSymbolInFallbackDatabase(const string& name) const { + if (fallback_database_ == NULL) return false; + + FileDescriptorProto file_proto; + if (!fallback_database_->FindFileContainingSymbol(name, &file_proto)) { + return false; + } + + if (tables_->FindFile(file_proto.name()) != NULL) { + // We've already loaded this file, and it apparently doesn't contain the + // symbol we're looking for. Some DescriptorDatabases return false + // positives. + return false; + } + + if (BuildFileFromDatabase(file_proto) == NULL) { + return false; + } + + return true; +} + +bool DescriptorPool::TryFindExtensionInFallbackDatabase( + const Descriptor* containing_type, int field_number) const { + if (fallback_database_ == NULL) return false; + + FileDescriptorProto file_proto; + if (!fallback_database_->FindFileContainingExtension( + containing_type->full_name(), field_number, &file_proto)) { + return false; + } + + if (tables_->FindFile(file_proto.name()) != NULL) { + // We've already loaded this file, and it apparently doesn't contain the + // extension we're looking for. Some DescriptorDatabases return false + // positives. + return false; + } + + if (BuildFileFromDatabase(file_proto) == NULL) { + return false; + } + + return true; +} + +// =================================================================== + +string FieldDescriptor::DefaultValueAsString(bool quote_string_type) const { + GOOGLE_CHECK(has_default_value()) << "No default value"; + switch (cpp_type()) { + case CPPTYPE_INT32: + return SimpleItoa(default_value_int32()); + break; + case CPPTYPE_INT64: + return SimpleItoa(default_value_int64()); + break; + case CPPTYPE_UINT32: + return SimpleItoa(default_value_uint32()); + break; + case CPPTYPE_UINT64: + return SimpleItoa(default_value_uint64()); + break; + case CPPTYPE_FLOAT: + return SimpleFtoa(default_value_float()); + break; + case CPPTYPE_DOUBLE: + return SimpleDtoa(default_value_double()); + break; + case CPPTYPE_BOOL: + return default_value_bool() ? "true" : "false"; + break; + case CPPTYPE_STRING: + if (quote_string_type) { + return "\"" + CEscape(default_value_string()) + "\""; + } else { + if (type() == TYPE_BYTES) { + return CEscape(default_value_string()); + } else { + return default_value_string(); + } + } + break; + case CPPTYPE_ENUM: + return default_value_enum()->name(); + break; + case CPPTYPE_MESSAGE: + GOOGLE_LOG(DFATAL) << "Messages can't have default values!"; + break; + } + GOOGLE_LOG(FATAL) << "Can't get here: failed to get default value as string"; + return ""; +} + +// CopyTo methods ==================================================== + +void FileDescriptor::CopyTo(FileDescriptorProto* proto) const { + proto->set_name(name()); + if (!package().empty()) proto->set_package(package()); + + for (int i = 0; i < dependency_count(); i++) { + proto->add_dependency(dependency(i)->name()); + } + + for (int i = 0; i < message_type_count(); i++) { + message_type(i)->CopyTo(proto->add_message_type()); + } + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->CopyTo(proto->add_enum_type()); + } + for (int i = 0; i < service_count(); i++) { + service(i)->CopyTo(proto->add_service()); + } + for (int i = 0; i < extension_count(); i++) { + extension(i)->CopyTo(proto->add_extension()); + } + + if (&options() != &FileOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void Descriptor::CopyTo(DescriptorProto* proto) const { + proto->set_name(name()); + + for (int i = 0; i < field_count(); i++) { + field(i)->CopyTo(proto->add_field()); + } + for (int i = 0; i < nested_type_count(); i++) { + nested_type(i)->CopyTo(proto->add_nested_type()); + } + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->CopyTo(proto->add_enum_type()); + } + for (int i = 0; i < extension_range_count(); i++) { + DescriptorProto::ExtensionRange* range = proto->add_extension_range(); + range->set_start(extension_range(i)->start); + range->set_end(extension_range(i)->end); + } + for (int i = 0; i < extension_count(); i++) { + extension(i)->CopyTo(proto->add_extension()); + } + + if (&options() != &MessageOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void FieldDescriptor::CopyTo(FieldDescriptorProto* proto) const { + proto->set_name(name()); + proto->set_number(number()); + proto->set_label(static_cast(label())); + proto->set_type(static_cast(type())); + + if (is_extension()) { + proto->set_extendee("."); + proto->mutable_extendee()->append(containing_type()->full_name()); + } + + if (cpp_type() == CPPTYPE_MESSAGE) { + proto->set_type_name("."); + proto->mutable_type_name()->append(message_type()->full_name()); + } else if (cpp_type() == CPPTYPE_ENUM) { + proto->set_type_name("."); + proto->mutable_type_name()->append(enum_type()->full_name()); + } + + if (has_default_value()) { + proto->set_default_value(DefaultValueAsString(false)); + } + + if (&options() != &FieldOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void EnumDescriptor::CopyTo(EnumDescriptorProto* proto) const { + proto->set_name(name()); + + for (int i = 0; i < value_count(); i++) { + value(i)->CopyTo(proto->add_value()); + } + + if (&options() != &EnumOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void EnumValueDescriptor::CopyTo(EnumValueDescriptorProto* proto) const { + proto->set_name(name()); + proto->set_number(number()); + + if (&options() != &EnumValueOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void ServiceDescriptor::CopyTo(ServiceDescriptorProto* proto) const { + proto->set_name(name()); + + for (int i = 0; i < method_count(); i++) { + method(i)->CopyTo(proto->add_method()); + } + + if (&options() != &ServiceOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void MethodDescriptor::CopyTo(MethodDescriptorProto* proto) const { + proto->set_name(name()); + + proto->set_input_type("."); + proto->mutable_input_type()->append(input_type()->full_name()); + proto->set_output_type("."); + proto->mutable_output_type()->append(output_type()->full_name()); + + if (&options() != &MethodOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +// DebugString methods =============================================== + +namespace { + +// Used by each of the option formatters. +bool RetrieveOptions(const Message &options, vector *option_entries) { + option_entries->clear(); + const Message::Reflection *reflection = options.GetReflection(); + vector fields; + reflection->ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + // Doesn't make sense to have message type fields here + if (fields[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + continue; + } + int count = 1; + bool repeated = false; + if (fields[i]->is_repeated()) { + count = reflection->FieldSize(fields[i]); + repeated = true; + } + for (int j = 0; j < count; j++) { + string fieldval; + TextFormat::PrintFieldValueToString(options, fields[i], + repeated ? count : -1, &fieldval); + option_entries->push_back(fields[i]->name() + " = " + fieldval); + } + } + return !option_entries->empty(); +} + +// Formats options that all appear together in brackets. Does not include +// brackets. +bool FormatBracketedOptions(const Message &options, string *output) { + vector all_options; + if (RetrieveOptions(options, &all_options)) { + output->append(JoinStrings(all_options, ", ")); + } + return !all_options.empty(); +} + +// Formats options one per line +bool FormatLineOptions(int depth, const Message &options, string *output) { + string prefix(depth * 2, ' '); + vector all_options; + if (RetrieveOptions(options, &all_options)) { + for (int i = 0; i < all_options.size(); i++) { + strings::SubstituteAndAppend(output, "$0option $1;\n", + prefix, all_options[i]); + } + } + return !all_options.empty(); +} + +} // anonymous namespace + +string FileDescriptor::DebugString() const { + string contents = "syntax = \"proto2\";\n\n"; + + for (int i = 0; i < dependency_count(); i++) { + strings::SubstituteAndAppend(&contents, "import \"$0\";\n", + dependency(i)->name()); + } + + if (!package().empty()) { + strings::SubstituteAndAppend(&contents, "package $0;\n\n", package()); + } + + if (FormatLineOptions(0, options(), &contents)) { + contents.append("\n"); // add some space if we had options + } + + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->DebugString(0, &contents); + contents.append("\n"); + } + + // Find all the 'group' type extensions; we will not output their nested + // definitions (those will be done with their group field descriptor). + set groups; + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->type() == FieldDescriptor::TYPE_GROUP) { + groups.insert(extension(i)->message_type()); + } + } + + for (int i = 0; i < message_type_count(); i++) { + if (groups.count(message_type(i)) == 0) { + strings::SubstituteAndAppend(&contents, "message $0", + message_type(i)->name()); + message_type(i)->DebugString(0, &contents); + contents.append("\n"); + } + } + + for (int i = 0; i < service_count(); i++) { + service(i)->DebugString(&contents); + contents.append("\n"); + } + + const Descriptor* containing_type = NULL; + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->containing_type() != containing_type) { + if (i > 0) contents.append("}\n\n"); + containing_type = extension(i)->containing_type(); + strings::SubstituteAndAppend(&contents, "extend .$0 {\n", + containing_type->full_name()); + } + extension(i)->DebugString(1, &contents); + } + if (extension_count() > 0) contents.append("}\n\n"); + + return contents; +} + +string Descriptor::DebugString() const { + string contents; + strings::SubstituteAndAppend(&contents, "message $0", name()); + DebugString(0, &contents); + return contents; +} + +void Descriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + ++depth; + contents->append(" {\n"); + + FormatLineOptions(depth, options(), contents); + + // Find all the 'group' types for fields and extensions; we will not output + // their nested definitions (those will be done with their group field + // descriptor). + set groups; + for (int i = 0; i < field_count(); i++) { + if (field(i)->type() == FieldDescriptor::TYPE_GROUP) { + groups.insert(field(i)->message_type()); + } + } + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->type() == FieldDescriptor::TYPE_GROUP) { + groups.insert(extension(i)->message_type()); + } + } + + for (int i = 0; i < nested_type_count(); i++) { + if (groups.count(nested_type(i)) == 0) { + strings::SubstituteAndAppend(contents, "$0 message $1", + prefix, nested_type(i)->name()); + nested_type(i)->DebugString(depth, contents); + } + } + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->DebugString(depth, contents); + } + for (int i = 0; i < field_count(); i++) { + field(i)->DebugString(depth, contents); + } + + for (int i = 0; i < extension_range_count(); i++) { + strings::SubstituteAndAppend(contents, "$0 extensions $1 to $2;\n", + prefix, + extension_range(i)->start, + extension_range(i)->end - 1); + } + + // Group extensions by what they extend, so they can be printed out together. + const Descriptor* containing_type = NULL; + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->containing_type() != containing_type) { + if (i > 0) strings::SubstituteAndAppend(contents, "$0 }\n", prefix); + containing_type = extension(i)->containing_type(); + strings::SubstituteAndAppend(contents, "$0 extend .$1 {\n", + prefix, containing_type->full_name()); + } + extension(i)->DebugString(depth + 1, contents); + } + if (extension_count() > 0) + strings::SubstituteAndAppend(contents, "$0 }\n", prefix); + + strings::SubstituteAndAppend(contents, "$0}\n", prefix); +} + +string FieldDescriptor::DebugString() const { + string contents; + int depth = 0; + if (is_extension()) { + strings::SubstituteAndAppend(&contents, "extend .$0 {\n", + containing_type()->full_name()); + depth = 1; + } + DebugString(depth, &contents); + if (is_extension()) { + contents.append("}\n"); + } + return contents; +} + +void FieldDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + string field_type; + switch (type()) { + case TYPE_MESSAGE: + field_type = "." + message_type()->full_name(); + break; + case TYPE_ENUM: + field_type = "." + enum_type()->full_name(); + break; + default: + field_type = kTypeToName[type()]; + } + + strings::SubstituteAndAppend(contents, "$0$1 $2 $3 = $4", + prefix, + kLabelToName[label()], + field_type, + type() == TYPE_GROUP ? message_type()->name() : + name(), + number()); + + bool bracketed = false; + if (has_default_value()) { + bracketed = true; + strings::SubstituteAndAppend(contents, " [default = $0", + DefaultValueAsString(true)); + } + + string formatted_options; + if (FormatBracketedOptions(options(), &formatted_options)) { + contents->append(bracketed ? ", " : " ["); + bracketed = true; + contents->append(formatted_options); + } + + if (bracketed) { + contents->append("]"); + } + + if (type() == TYPE_GROUP) { + message_type()->DebugString(depth, contents); + } else { + contents->append(";\n"); + } +} + +string EnumDescriptor::DebugString() const { + string contents; + DebugString(0, &contents); + return contents; +} + +void EnumDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + ++depth; + strings::SubstituteAndAppend(contents, "$0enum $1 {\n", + prefix, name()); + + FormatLineOptions(depth, options(), contents); + + for (int i = 0; i < value_count(); i++) { + value(i)->DebugString(depth, contents); + } + strings::SubstituteAndAppend(contents, "$0}\n", prefix); +} + +string EnumValueDescriptor::DebugString() const { + string contents; + DebugString(0, &contents); + return contents; +} + +void EnumValueDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + strings::SubstituteAndAppend(contents, "$0$1 = $2", + prefix, name(), number()); + + string formatted_options; + if (FormatBracketedOptions(options(), &formatted_options)) { + strings::SubstituteAndAppend(contents, " [$0]", formatted_options); + } + contents->append(";\n"); +} + +string ServiceDescriptor::DebugString() const { + string contents; + DebugString(&contents); + return contents; +} + +void ServiceDescriptor::DebugString(string *contents) const { + strings::SubstituteAndAppend(contents, "service $0 {\n", name()); + + FormatLineOptions(1, options(), contents); + + for (int i = 0; i < method_count(); i++) { + method(i)->DebugString(1, contents); + } + + contents->append("}\n"); +} + +string MethodDescriptor::DebugString() const { + string contents; + DebugString(0, &contents); + return contents; +} + +void MethodDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + ++depth; + strings::SubstituteAndAppend(contents, "$0rpc $1(.$2) returns (.$3)", + prefix, name(), + input_type()->full_name(), + output_type()->full_name()); + + string formatted_options; + if (FormatLineOptions(depth, options(), &formatted_options)) { + strings::SubstituteAndAppend(contents, " {\n$0$1}\n", + formatted_options, prefix); + } else { + contents->append(";\n"); + } +} +// =================================================================== + +class DescriptorBuilder { + public: + DescriptorBuilder(const DescriptorPool* pool, + DescriptorPool::Tables* tables, + DescriptorPool::ErrorCollector* error_collector); + ~DescriptorBuilder(); + + const FileDescriptor* BuildFile(const FileDescriptorProto& proto); + + private: + const DescriptorPool* pool_; + DescriptorPool::Tables* tables_; // for convenience + DescriptorPool::ErrorCollector* error_collector_; + bool had_errors_; + string filename_; + FileDescriptor* file_; + + // If LookupSymbol() finds a symbol that is in a file which is not a declared + // dependency of this file, it will fail, but will set + // possible_undeclared_dependency_ to point at that file. This is only used + // by AddNotDefinedError() to report a more useful error message. + const FileDescriptor* possible_undeclared_dependency_; + + void AddError(const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& error); + + // Adds an error indicating that undefined_symbol was not defined. Must + // only be called after LookupSymbol() fails. + void AddNotDefinedError( + const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& undefined_symbol); + + // Silly helper which determines if the given file is in the given package. + // I.e., either file->package() == package_name or file->package() is a + // nested package within package_name. + bool IsInPackage(const FileDescriptor* file, const string& package_name); + + // Like tables_->FindSymbol(), but additionally: + // - Search the pool's underlay if not found in tables_. + // - Insure that the resulting Symbol is from one of the file's declared + // dependencies. + Symbol FindSymbol(const string& name); + + // Like FindSymbol(), but looks up the name relative to some other symbol + // name. This first searches syblings of relative_to, then siblings of its + // parents, etc. For example, LookupSymbol("foo.bar", "baz.qux.corge") makes + // the following calls, returning the first non-null result: + // FindSymbol("baz.qux.foo.bar"), FindSymbol("baz.foo.bar"), + // FindSymbol("foo.bar"). + Symbol LookupSymbol(const string& name, const string& relative_to); + + // Calls tables_->AddSymbol() and records an error if it fails. + void AddSymbol(const string& full_name, + const void* parent, const string& name, + const Message& proto, Symbol symbol); + + // Like AddSymbol(), but succeeds if the symbol is already defined as long + // as the existing definition is also a package (because it's OK to define + // the same package in two different files). Also adds all parents of the + // packgae to the symbol table (e.g. AddPackage("foo.bar", ...) will add + // "foo.bar" and "foo" to the table). + void AddPackage(const string& name, const Message& proto, + const FileDescriptor* file); + + // Checks that the symbol name contains only alphanumeric characters and + // underscores. Records an error otherwise. + void ValidateSymbolName(const string& name, const string& full_name, + const Message& proto); + + // Used by BUILD_ARRAY macro (below) to avoid having to have the type + // specified as a macro parameter. + template + inline void AllocateArray(int size, Type** output) { + *output = tables_->AllocateArray(size); + } + + // These methods all have the same signature for the sake of the BUILD_ARRAY + // macro, below. + void BuildMessage(const DescriptorProto& proto, + const Descriptor* parent, + Descriptor* result); + void BuildFieldOrExtension(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result, + bool is_extension); + void BuildField(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result) { + BuildFieldOrExtension(proto, parent, result, false); + } + void BuildExtension(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result) { + BuildFieldOrExtension(proto, parent, result, true); + } + void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto, + const Descriptor* parent, + Descriptor::ExtensionRange* result); + void BuildEnum(const EnumDescriptorProto& proto, + const Descriptor* parent, + EnumDescriptor* result); + void BuildEnumValue(const EnumValueDescriptorProto& proto, + const EnumDescriptor* parent, + EnumValueDescriptor* result); + void BuildService(const ServiceDescriptorProto& proto, + const void* dummy, + ServiceDescriptor* result); + void BuildMethod(const MethodDescriptorProto& proto, + const ServiceDescriptor* parent, + MethodDescriptor* result); + + void CrossLinkFile(FileDescriptor* file, const FileDescriptorProto& proto); + void CrossLinkMessage(Descriptor* message, const DescriptorProto& proto); + void CrossLinkField(FieldDescriptor* field, + const FieldDescriptorProto& proto); + void CrossLinkService(ServiceDescriptor* service, + const ServiceDescriptorProto& proto); + void CrossLinkMethod(MethodDescriptor* method, + const MethodDescriptorProto& proto); + void CrossLinkMapKey(FieldDescriptor* field, + const FieldDescriptorProto& proto); +}; + +const FileDescriptor* DescriptorPool::BuildFile( + const FileDescriptorProto& proto) { + GOOGLE_CHECK(fallback_database_ == NULL) + << "Cannot call BuildFile on a DescriptorPool that uses a " + "DescriptorDatabase. You must instead find a way to get your file " + "into the underlying database."; + GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. + return DescriptorBuilder(this, tables_.get(), NULL).BuildFile(proto); +} + +const FileDescriptor* DescriptorPool::BuildFileCollectingErrors( + const FileDescriptorProto& proto, + ErrorCollector* error_collector) { + GOOGLE_CHECK(fallback_database_ == NULL) + << "Cannot call BuildFile on a DescriptorPool that uses a " + "DescriptorDatabase. You must instead find a way to get your file " + "into the underlying database."; + GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. + return DescriptorBuilder(this, tables_.get(), + error_collector).BuildFile(proto); +} + +const FileDescriptor* DescriptorPool::BuildFileFromDatabase( + const FileDescriptorProto& proto) const { + mutex_->AssertHeld(); + return DescriptorBuilder(this, tables_.get(), + default_error_collector_).BuildFile(proto); +} + +const FileDescriptor* DescriptorPool::InternalBuildGeneratedFile( + const void* data, int size) { + // So, this function is called in the process of initializing the + // descriptors for generated proto classes. Each generated .pb.cc file + // has an internal procedure called BuildDescriptors() which is called the + // first time one of its descriptors is accessed, and that function calls + // this one in order to parse the raw bytes of the FileDescriptorProto + // representing the file. + // + // Note, though, that FileDescriptorProto is itself a generated protocol + // message. So, when we attempt to construct one below, it will attempt + // to initialize its own descriptors via its own BuildDescriptors() method. + // This will in turn cause InternalBuildGeneratedFile() to build + // descriptor.proto's descriptors. + // + // We are saved from an infinite loop by the fact that BuildDescriptors() + // only does anything the first time it is called. That is, the first few + // lines of any BuildDescriptors() procedure look like this: + // void BuildDescriptors() { + // static bool already_here = false; + // if (already_here) return; + // already_here = true; + // ... + // So, when descriptor.pb.cc's BuildDescriptors() is called recursively, it + // will end up just returning without doing anything. The result is that + // all of the descriptors for FileDescriptorProto and friends will just be + // NULL. + // + // Luckily, it turns out that our limited use of FileDescriptorProto within + // InternalBuildGeneratedFile() does not require that its descriptors be + // initialized. So, this ends up working. As soon as + // InternalBuildGeneratedFile() returns, the descriptors will be initialized + // by the original call to BuildDescriptors(), and everything will be happy + // again. + // + // If this turns out to be too fragile a hack, there are other ways we + // can accomplish bootstrapping here (like building the descriptor for + // descriptor.proto manually), but if this works then it's a lot easier. + // + // Note that because this is only triggered at static initialization time, + // there are no thread-safety concerns here. + FileDescriptorProto proto; + GOOGLE_CHECK(proto.ParseFromArray(data, size)); + const FileDescriptor* result = BuildFile(proto); + GOOGLE_CHECK(result != NULL); + + return result; +} + +DescriptorBuilder::DescriptorBuilder( + const DescriptorPool* pool, + DescriptorPool::Tables* tables, + DescriptorPool::ErrorCollector* error_collector) + : pool_(pool), + tables_(tables), + error_collector_(error_collector), + had_errors_(false), + possible_undeclared_dependency_(NULL) {} + +DescriptorBuilder::~DescriptorBuilder() {} + +void DescriptorBuilder::AddError( + const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& error) { + if (error_collector_ == NULL) { + if (!had_errors_) { + GOOGLE_LOG(ERROR) << "Invalid proto descriptor for file \"" << filename_ + << "\":"; + } + GOOGLE_LOG(ERROR) << " " << element_name << ": " << error; + } else { + error_collector_->AddError(filename_, element_name, + &descriptor, location, error); + } + had_errors_ = true; +} + +void DescriptorBuilder::AddNotDefinedError( + const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& undefined_symbol) { + if (possible_undeclared_dependency_ == NULL) { + AddError(element_name, descriptor, location, + "\"" + undefined_symbol + "\" is not defined."); + } else { + AddError(element_name, descriptor, location, + "\"" + undefined_symbol + "\" seems to be defined in \"" + + possible_undeclared_dependency_->name() + "\", which is not " + "imported by \"" + filename_ + "\". To use it here, please " + "add the necessary import."); + } +} + +bool DescriptorBuilder::IsInPackage(const FileDescriptor* file, + const string& package_name) { + return HasPrefixString(file->package(), package_name) && + (file->package().size() == package_name.size() || + file->package()[package_name.size()] == '.'); +} + +Symbol DescriptorBuilder::FindSymbol(const string& name) { + Symbol result; + + // We need to search our pool and all its underlays. + const DescriptorPool* pool = pool_; + while (true) { + // If we are looking at an underlay, we must lock its mutex_, since we are + // accessing the underlay's tables_ dircetly. + MutexLockMaybe lock((pool == pool_) ? NULL : pool->mutex_); + + // Note that we don't have to check fallback_database_ here because the + // symbol has to be in one of its file's direct dependencies, and we have + // already loaded those by the time we get here. + result = pool->tables_->FindSymbol(name); + if (!result.IsNull()) break; + if (pool->underlay_ == NULL) return kNullSymbol; + pool = pool->underlay_; + } + + if (!pool_->enforce_dependencies_) { + // Hack for CompilerUpgrader. + return result; + } + + // Only find symbols which were defined in this file or one of its + // dependencies. + const FileDescriptor* file = result.GetFile(); + if (file == file_) return result; + for (int i = 0; i < file_->dependency_count(); i++) { + if (file == file_->dependency(i)) return result; + } + + if (result.type == Symbol::PACKAGE) { + // Arg, this is overcomplicated. The symbol is a package name. It could + // be that the package was defined in multiple files. result.GetFile() + // returns the first file we saw that used this package. We've determined + // that that file is not a direct dependency of the file we are currently + // building, but it could be that some other file which *is* a direct + // dependency also defines the same package. We can't really rule out this + // symbol unless none of the dependencies define it. + if (IsInPackage(file_, name)) return result; + for (int i = 0; i < file_->dependency_count(); i++) { + if (IsInPackage(file_->dependency(i), name)) return result; + } + } + + possible_undeclared_dependency_ = file; + return kNullSymbol; +} + +Symbol DescriptorBuilder::LookupSymbol( + const string& name, const string& relative_to) { + possible_undeclared_dependency_ = NULL; + + if (name.size() > 0 && name[0] == '.') { + // Fully-qualified name. + return FindSymbol(name.substr(1)); + } + + // If name is something like "Foo.Bar.baz", and symbols named "Foo" are + // defined in multiple parent scopes, we only want to find "Bar.baz" in the + // innermost one. E.g., the following should produce an error: + // message Bar { message Baz {} } + // message Foo { + // message Bar { + // } + // optional Bar.Baz baz = 1; + // } + // So, we look for just "Foo" first, then look for "Bar.baz" within it if + // found. + int name_dot_pos = name.find_first_of('.'); + string first_part_of_name; + if (name_dot_pos == string::npos) { + first_part_of_name = name; + } else { + first_part_of_name = name.substr(0, name_dot_pos); + } + + string scope_to_try(relative_to); + + while (true) { + // Chop off the last component of the scope. + string::size_type dot_pos = scope_to_try.find_last_of('.'); + if (dot_pos == string::npos) { + return FindSymbol(name); + } else { + scope_to_try.erase(dot_pos); + } + + // Append ".first_part_of_name" and try to find. + string::size_type old_size = scope_to_try.size(); + scope_to_try.append(1, '.'); + scope_to_try.append(first_part_of_name); + Symbol result = FindSymbol(scope_to_try); + if (!result.IsNull()) { + if (first_part_of_name.size() < name.size()) { + // name is a compound symbol, of which we only found the first part. + // Now try to look up the rest of it. + scope_to_try.append(name, first_part_of_name.size(), + name.size() - first_part_of_name.size()); + result = FindSymbol(scope_to_try); + } + return result; + } + + // Not found. Remove the name so we can try again. + scope_to_try.erase(old_size); + } +} + +void DescriptorBuilder::AddSymbol( + const string& full_name, const void* parent, const string& name, + const Message& proto, Symbol symbol) { + // If the caller passed NULL for the parent, the symbol is at file scope. + // Use its file as the parent instead. + if (parent == NULL) parent = file_; + + if (!tables_->AddSymbol(full_name, parent, name, symbol)) { + const FileDescriptor* other_file = tables_->FindSymbol(full_name).GetFile(); + if (other_file == file_) { + string::size_type dot_pos = full_name.find_last_of('.'); + if (dot_pos == string::npos) { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + full_name + "\" is already defined."); + } else { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + full_name.substr(dot_pos + 1) + + "\" is already defined in \"" + + full_name.substr(0, dot_pos) + "\"."); + } + } else { + // Symbol seems to have been defined in a different file. + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + full_name + "\" is already defined in file \"" + + other_file->name() + "\"."); + } + } +} + +void DescriptorBuilder::AddPackage( + const string& name, const Message& proto, const FileDescriptor* file) { + if (tables_->AddSymbol(name, NULL, name, Symbol(file))) { + // Success. Also add parent package, if any. + string::size_type dot_pos = name.find_last_of('.'); + if (dot_pos == string::npos) { + // No parents. + ValidateSymbolName(name, name, proto); + } else { + // Has parent. + string* parent_name = tables_->AllocateString(name.substr(0, dot_pos)); + AddPackage(*parent_name, proto, file); + ValidateSymbolName(name.substr(dot_pos + 1), name, proto); + } + } else { + Symbol existing_symbol = tables_->FindSymbol(name); + // It's OK to redefine a package. + if (existing_symbol.type != Symbol::PACKAGE) { + // Symbol seems to have been defined in a different file. + AddError(name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + name + "\" is already defined (as something other than " + "a package) in file \"" + existing_symbol.GetFile()->name() + + "\"."); + } + } +} + +void DescriptorBuilder::ValidateSymbolName( + const string& name, const string& full_name, const Message& proto) { + if (name.empty()) { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "Missing name."); + } else { + for (int i = 0; i < name.size(); i++) { + // I don't trust isalnum() due to locales. :( + if ((name[i] < 'a' || 'z' < name[i]) && + (name[i] < 'A' || 'Z' < name[i]) && + (name[i] < '0' || '9' < name[i]) && + (name[i] != '_')) { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + name + "\" is not a valid identifier."); + } + } + } +} + +// ------------------------------------------------------------------- + +// A common pattern: We want to convert a repeated field in the descriptor +// to an array of values, calling some method to build each value. +#define BUILD_ARRAY(INPUT, OUTPUT, NAME, METHOD, PARENT) \ + OUTPUT->NAME##_count_ = INPUT.NAME##_size(); \ + AllocateArray(INPUT.NAME##_size(), &OUTPUT->NAME##s_); \ + for (int i = 0; i < INPUT.NAME##_size(); i++) { \ + METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i); \ + } + +const FileDescriptor* DescriptorBuilder::BuildFile( + const FileDescriptorProto& proto) { + filename_ = proto.name(); + + // Check to see if this file is already on the pending files list. + // TODO(kenton): Allow recursive imports? It may not work with some + // (most?) programming languages. E.g., in C++, a forward declaration + // of a type is not sufficient to allow it to be used even in a + // generated header file due to inlining. This could perhaps be + // worked around using tricks involving inserting #include statements + // mid-file, but that's pretty ugly, and I'm pretty sure there are + // some languages out there that do not allow recursive dependencies + // at all. + for (int i = 0; i < tables_->pending_files_.size(); i++) { + if (tables_->pending_files_[i] == proto.name()) { + string error_message("File recursively imports itself: "); + for (; i < tables_->pending_files_.size(); i++) { + error_message.append(tables_->pending_files_[i]); + error_message.append(" -> "); + } + error_message.append(proto.name()); + + AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, + error_message); + return NULL; + } + } + + // If we have a fallback_database_, attempt to load all dependencies now, + // before checkpointing tables_. This avoids confusion with recursive + // checkpoints. + if (pool_->fallback_database_ != NULL) { + tables_->pending_files_.push_back(proto.name()); + for (int i = 0; i < proto.dependency_size(); i++) { + if (tables_->FindFile(proto.dependency(i)) == NULL && + (pool_->underlay_ == NULL || + pool_->underlay_->FindFileByName(proto.dependency(i)) == NULL)) { + // We don't care what this returns since we'll find out below anyway. + pool_->TryFindFileInFallbackDatabase(proto.dependency(i)); + } + } + tables_->pending_files_.pop_back(); + } + + // Checkpoint the tables so that we can roll back if something goes wrong. + tables_->Checkpoint(); + + FileDescriptor* result = tables_->Allocate(); + file_ = result; + + if (!proto.has_name()) { + AddError("", proto, DescriptorPool::ErrorCollector::OTHER, + "Missing field: FileDescriptorProto.name."); + } + + result->name_ = tables_->AllocateString(proto.name()); + result->package_ = tables_->AllocateString(proto.package()); + result->pool_ = pool_; + + // Add to tables. + if (!tables_->AddFile(result)) { + AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, + "A file with this name is already in the pool."); + // Bail out early so that if this is actually the exact same file, we + // don't end up reporting that every single symbol is already defined. + tables_->Rollback(); + return NULL; + } + if (!result->package().empty()) { + AddPackage(result->package(), proto, result); + } + + // Make sure all dependencies are loaded. + set seen_dependencies; + result->dependency_count_ = proto.dependency_size(); + result->dependencies_ = + tables_->AllocateArray(proto.dependency_size()); + for (int i = 0; i < proto.dependency_size(); i++) { + if (!seen_dependencies.insert(proto.dependency(i)).second) { + AddError(proto.name(), proto, + DescriptorPool::ErrorCollector::OTHER, + "Import \"" + proto.dependency(i) + "\" was listed twice."); + } + + const FileDescriptor* dependency = tables_->FindFile(proto.dependency(i)); + if (dependency == NULL && pool_->underlay_ != NULL) { + dependency = pool_->underlay_->FindFileByName(proto.dependency(i)); + } + + if (dependency == NULL) { + string message; + if (pool_->fallback_database_ == NULL) { + message = "Import \"" + proto.dependency(i) + + "\" has not been loaded."; + } else { + message = "Import \"" + proto.dependency(i) + + "\" was not found or had errors."; + } + AddError(proto.name(), proto, + DescriptorPool::ErrorCollector::OTHER, + message); + } + + result->dependencies_[i] = dependency; + } + + // Convert children. + BUILD_ARRAY(proto, result, message_type, BuildMessage , NULL); + BUILD_ARRAY(proto, result, enum_type , BuildEnum , NULL); + BUILD_ARRAY(proto, result, service , BuildService , NULL); + BUILD_ARRAY(proto, result, extension , BuildExtension, NULL); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &FileOptions::default_instance(); + } else { + FileOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + // Cross-link. + CrossLinkFile(result, proto); + + if (had_errors_) { + tables_->Rollback(); + return NULL; + } else { + tables_->Checkpoint(); + return result; + } +} + +void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, + const Descriptor* parent, + Descriptor* result) { + const string& scope = (parent == NULL) ? + file_->package() : parent->full_name(); + string* full_name = tables_->AllocateString(scope); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + result->containing_type_ = parent; + + BUILD_ARRAY(proto, result, field , BuildField , result); + BUILD_ARRAY(proto, result, nested_type , BuildMessage , result); + BUILD_ARRAY(proto, result, enum_type , BuildEnum , result); + BUILD_ARRAY(proto, result, extension_range, BuildExtensionRange, result); + BUILD_ARRAY(proto, result, extension , BuildExtension , result); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &MessageOptions::default_instance(); + } else { + MessageOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); + + // Check that no fields have numbers in extension ranges. + for (int i = 0; i < result->field_count(); i++) { + const FieldDescriptor* field = result->field(i); + for (int j = 0; j < result->extension_range_count(); j++) { + const Descriptor::ExtensionRange* range = result->extension_range(j); + if (range->start <= field->number() && field->number() < range->end) { + AddError(field->full_name(), proto.extension_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute( + "Extension range $0 to $1 includes field \"$2\" ($3).", + range->start, range->end - 1, + field->name(), field->number())); + } + } + } + + // Check that extension ranges don't overlap. + for (int i = 0; i < result->extension_range_count(); i++) { + const Descriptor::ExtensionRange* range1 = result->extension_range(i); + for (int j = i + 1; j < result->extension_range_count(); j++) { + const Descriptor::ExtensionRange* range2 = result->extension_range(j); + if (range1->end > range2->start && range2->end > range1->start) { + AddError(result->full_name(), proto.extension_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension range $0 to $1 overlaps with " + "already-defined range $2 to $3.", + range2->start, range2->end - 1, + range1->start, range1->end - 1)); + } + } + } +} + +void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result, + bool is_extension) { + const string& scope = (parent == NULL) ? + file_->package() : parent->full_name(); + string* full_name = tables_->AllocateString(scope); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + result->number_ = proto.number(); + result->type_ = static_cast(proto.type()); + result->label_ = static_cast(proto.label()); + result->is_extension_ = is_extension; + + // Some of these may be filled in when cross-linking. + result->containing_type_ = NULL; + result->extension_scope_ = NULL; + result->experimental_map_key_ = NULL; + result->message_type_ = NULL; + result->enum_type_ = NULL; + + result->has_default_value_ = proto.has_default_value(); + if (proto.has_type()) { + if (proto.has_default_value()) { + char* end_pos = NULL; + switch (result->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + result->default_value_int32_ = + strtol(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_INT64: + result->default_value_int64_ = + strto64(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_UINT32: + result->default_value_uint32_ = + strtoul(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_UINT64: + result->default_value_uint64_ = + strtou64(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_FLOAT: + result->default_value_float_ = + NoLocaleStrtod(proto.default_value().c_str(), &end_pos); + break; + case FieldDescriptor::CPPTYPE_DOUBLE: + result->default_value_double_ = + NoLocaleStrtod(proto.default_value().c_str(), &end_pos); + break; + case FieldDescriptor::CPPTYPE_BOOL: + if (proto.default_value() == "true") { + result->default_value_bool_ = true; + } else if (proto.default_value() == "false") { + result->default_value_bool_ = false; + } else { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Boolean default must be true or false."); + } + break; + case FieldDescriptor::CPPTYPE_ENUM: + // This will be filled in when cross-linking. + result->default_value_enum_ = NULL; + break; + case FieldDescriptor::CPPTYPE_STRING: + if (result->type() == FieldDescriptor::TYPE_BYTES) { + result->default_value_string_ = tables_->AllocateString( + UnescapeCEscapeString(proto.default_value())); + } else { + result->default_value_string_ = + tables_->AllocateString(proto.default_value()); + } + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Messages can't have default values."); + result->has_default_value_ = false; + break; + } + + if (end_pos != NULL) { + // end_pos is only set non-NULL by the parsers for numeric types, above. + // This checks that the default was non-empty and had no extra junk + // after the end of the number. + if (proto.default_value().empty() || *end_pos != '\0') { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Couldn't parse default value."); + } + } + } else { + // No explicit default value + switch (result->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + result->default_value_int32_ = 0; + break; + case FieldDescriptor::CPPTYPE_INT64: + result->default_value_int64_ = 0; + break; + case FieldDescriptor::CPPTYPE_UINT32: + result->default_value_uint32_ = 0; + break; + case FieldDescriptor::CPPTYPE_UINT64: + result->default_value_uint64_ = 0; + break; + case FieldDescriptor::CPPTYPE_FLOAT: + result->default_value_float_ = 0.0f; + break; + case FieldDescriptor::CPPTYPE_DOUBLE: + result->default_value_double_ = 0.0; + break; + case FieldDescriptor::CPPTYPE_BOOL: + result->default_value_bool_ = false; + break; + case FieldDescriptor::CPPTYPE_ENUM: + // This will be filled in when cross-linking. + result->default_value_enum_ = NULL; + break; + case FieldDescriptor::CPPTYPE_STRING: + result->default_value_string_ = &kEmptyString; + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + break; + } + } + } + + if (result->number() <= 0) { + AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER, + "Field numbers must be positive integers."); + } else if (result->number() > FieldDescriptor::kMaxNumber) { + AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Field numbers cannot be greater than $0.", + FieldDescriptor::kMaxNumber)); + } else if (result->number() >= FieldDescriptor::kFirstReservedNumber && + result->number() <= FieldDescriptor::kLastReservedNumber) { + AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute( + "Field numbers $0 through $1 are reserved for the protocol " + "buffer library implementation.", + FieldDescriptor::kFirstReservedNumber, + FieldDescriptor::kLastReservedNumber)); + } + + if (is_extension) { + if (!proto.has_extendee()) { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + "FieldDescriptorProto.extendee not set for extension field."); + } + + result->extension_scope_ = parent; + } else { + if (proto.has_extendee()) { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + "FieldDescriptorProto.extendee set for non-extension field."); + } + + result->containing_type_ = parent; + } + + // Copy options. + if (!proto.has_options()) { + result->options_ = &FieldOptions::default_instance(); + } else { + FieldOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); +} + +void DescriptorBuilder::BuildExtensionRange( + const DescriptorProto::ExtensionRange& proto, + const Descriptor* parent, + Descriptor::ExtensionRange* result) { + result->start = proto.start(); + result->end = proto.end(); + if (result->start <= 0) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + "Extension numbers must be positive integers."); + } + + if (result->end > FieldDescriptor::kMaxNumber + 1) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension numbers cannot be greater than $0.", + FieldDescriptor::kMaxNumber)); + } + + if (result->start >= result->end) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + "Extension range end number must be greater than start number."); + } +} + +void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto, + const Descriptor* parent, + EnumDescriptor* result) { + const string& scope = (parent == NULL) ? + file_->package() : parent->full_name(); + string* full_name = tables_->AllocateString(scope); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + result->containing_type_ = parent; + + if (proto.value_size() == 0) { + // We cannot allow enums with no values because this would mean there + // would be no valid default value for fields of this type. + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::NAME, + "Enums must contain at least one value."); + } + + BUILD_ARRAY(proto, result, value, BuildEnumValue, result); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &EnumOptions::default_instance(); + } else { + EnumOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); +} + +void DescriptorBuilder::BuildEnumValue(const EnumValueDescriptorProto& proto, + const EnumDescriptor* parent, + EnumValueDescriptor* result) { + result->name_ = tables_->AllocateString(proto.name()); + result->number_ = proto.number(); + result->type_ = parent; + + // Note: full_name for enum values is a sibling to the parent's name, not a + // child of it. + string* full_name = tables_->AllocateString(*parent->full_name_); + full_name->resize(full_name->size() - parent->name_->size()); + full_name->append(*result->name_); + result->full_name_ = full_name; + + ValidateSymbolName(proto.name(), *full_name, proto); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &EnumValueOptions::default_instance(); + } else { + EnumValueOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + // Again, enum values are weird because we makes them appear as siblings + // of the enum type instead of children of it. So, we use + // parent->containing_type() as the value's parent. + AddSymbol(result->full_name(), parent->containing_type(), result->name(), + proto, Symbol(result)); + + // However, we also want to be able to search for values within a single + // enum type, so we add it as a child of the enum type itself, too. + // Note: This could fail, but if it does, the error has already been + // reported by the above AddSymbol() call, so we ignore the return code. + tables_->AddAliasUnderParent(parent, result->name(), Symbol(result)); + + // An enum is allowed to define two numbers that refer to the same value. + // FindValueByNumber() should return the first such value, so we simply + // ignore AddEnumValueByNumber()'s return code. + tables_->AddEnumValueByNumber(result); +} + +void DescriptorBuilder::BuildService(const ServiceDescriptorProto& proto, + const void* dummy, + ServiceDescriptor* result) { + string* full_name = tables_->AllocateString(file_->package()); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + + BUILD_ARRAY(proto, result, method, BuildMethod, result); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &ServiceOptions::default_instance(); + } else { + ServiceOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), NULL, result->name(), + proto, Symbol(result)); +} + +void DescriptorBuilder::BuildMethod(const MethodDescriptorProto& proto, + const ServiceDescriptor* parent, + MethodDescriptor* result) { + result->name_ = tables_->AllocateString(proto.name()); + result->service_ = parent; + + string* full_name = tables_->AllocateString(parent->full_name()); + full_name->append(1, '.'); + full_name->append(*result->name_); + result->full_name_ = full_name; + + ValidateSymbolName(proto.name(), *full_name, proto); + + // These will be filled in when cross-linking. + result->input_type_ = NULL; + result->output_type_ = NULL; + + // Copy options. + if (!proto.has_options()) { + result->options_ = &MethodOptions::default_instance(); + } else { + MethodOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); +} + +#undef BUILD_ARRAY + +// ------------------------------------------------------------------- + +void DescriptorBuilder::CrossLinkFile( + FileDescriptor* file, const FileDescriptorProto& proto) { + for (int i = 0; i < file->message_type_count(); i++) { + CrossLinkMessage(&file->message_types_[i], proto.message_type(i)); + } + + for (int i = 0; i < file->extension_count(); i++) { + CrossLinkField(&file->extensions_[i], proto.extension(i)); + } + + for (int i = 0; i < file->service_count(); i++) { + CrossLinkService(&file->services_[i], proto.service(i)); + } +} + +void DescriptorBuilder::CrossLinkMessage( + Descriptor* message, const DescriptorProto& proto) { + for (int i = 0; i < message->nested_type_count(); i++) { + CrossLinkMessage(&message->nested_types_[i], proto.nested_type(i)); + } + + for (int i = 0; i < message->field_count(); i++) { + CrossLinkField(&message->fields_[i], proto.field(i)); + } + + for (int i = 0; i < message->extension_count(); i++) { + CrossLinkField(&message->extensions_[i], proto.extension(i)); + } +} + +void DescriptorBuilder::CrossLinkField( + FieldDescriptor* field, const FieldDescriptorProto& proto) { + if (proto.has_extendee()) { + Symbol extendee = LookupSymbol(proto.extendee(), field->full_name()); + if (extendee.IsNull()) { + AddNotDefinedError(field->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + proto.extendee()); + return; + } else if (extendee.type != Symbol::MESSAGE) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + "\"" + proto.extendee() + "\" is not a message type."); + return; + } + field->containing_type_ = extendee.descriptor; + + if (!field->containing_type()->IsExtensionNumber(field->number())) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("\"$0\" does not declare $1 as an " + "extension number.", + field->containing_type()->full_name(), + field->number())); + } + } + + if (proto.has_type_name()) { + Symbol type = LookupSymbol(proto.type_name(), field->full_name()); + if (type.IsNull()) { + AddNotDefinedError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + proto.type_name()); + return; + } + + if (!proto.has_type()) { + // Choose field type based on symbol. + if (type.type == Symbol::MESSAGE) { + field->type_ = FieldDescriptor::TYPE_MESSAGE; + } else if (type.type == Symbol::ENUM) { + field->type_ = FieldDescriptor::TYPE_ENUM; + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "\"" + proto.type_name() + "\" is not a type."); + return; + } + } + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (type.type != Symbol::MESSAGE) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "\"" + proto.type_name() + "\" is not a message type."); + return; + } + field->message_type_ = type.descriptor; + + if (field->has_default_value()) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Messages can't have default values."); + } + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + if (type.type != Symbol::ENUM) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "\"" + proto.type_name() + "\" is not an enum type."); + return; + } + field->enum_type_ = type.enum_descriptor; + + if (field->has_default_value()) { + // We can't just use field->enum_type()->FindValueByName() here + // because that locks the pool's mutex, which we have already locked + // at this point. + Symbol default_value = + LookupSymbol(proto.default_value(), field->enum_type()->full_name()); + + if (default_value.type == Symbol::ENUM_VALUE && + default_value.enum_value_descriptor->type() == field->enum_type()) { + field->default_value_enum_ = default_value.enum_value_descriptor; + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Enum type \"" + field->enum_type()->full_name() + + "\" has no value named \"" + proto.default_value() + "\"."); + } + } else if (field->enum_type()->value_count() > 0) { + // All enums must have at least one value, or we would have reported + // an error elsewhere. We use the first defined value as the default + // if a default is not explicitly defined. + field->default_value_enum_ = field->enum_type()->value(0); + } + } else { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Field with primitive type has type_name."); + } + } else { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || + field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Field with message or enum type missing type_name."); + } + } + + if (proto.has_options() && proto.options().has_experimental_map_key()) { + CrossLinkMapKey(field, proto); + } + + // Add the field to the fields-by-number table. + // Note: We have to do this *after* cross-linking because extensions do not + // know their containing type until now. + if (!tables_->AddFieldByNumber(field)) { + const FieldDescriptor* conflicting_field = + tables_->FindFieldByNumber(field->containing_type(), field->number()); + if (field->is_extension()) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension number $0 has already been used " + "in \"$1\" by extension \"$2\".", + field->number(), + field->containing_type()->full_name(), + conflicting_field->full_name())); + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Field number $0 has already been used in " + "\"$1\" by field \"$2\".", + field->number(), + field->containing_type()->full_name(), + conflicting_field->name())); + } + } + + // Note: Default instance may not yet be initialized here, so we have to + // avoid reading from it. + if (field->containing_type_ != NULL && + &field->containing_type()->options() != + &MessageOptions::default_instance() && + field->containing_type()->options().message_set_wire_format()) { + if (field->is_extension()) { + if (!field->is_optional() || + field->type() != FieldDescriptor::TYPE_MESSAGE) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "Extensions of MessageSets must be optional messages."); + } + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NAME, + "MessageSets cannot have fields, only extensions."); + } + } +} + +void DescriptorBuilder::CrossLinkService( + ServiceDescriptor* service, const ServiceDescriptorProto& proto) { + for (int i = 0; i < service->method_count(); i++) { + CrossLinkMethod(&service->methods_[i], proto.method(i)); + } +} + +void DescriptorBuilder::CrossLinkMethod( + MethodDescriptor* method, const MethodDescriptorProto& proto) { + Symbol input_type = LookupSymbol(proto.input_type(), method->full_name()); + if (input_type.IsNull()) { + AddNotDefinedError(method->full_name(), proto, + DescriptorPool::ErrorCollector::INPUT_TYPE, + proto.input_type()); + } else if (input_type.type != Symbol::MESSAGE) { + AddError(method->full_name(), proto, + DescriptorPool::ErrorCollector::INPUT_TYPE, + "\"" + proto.input_type() + "\" is not a message type."); + } else { + method->input_type_ = input_type.descriptor; + } + + Symbol output_type = LookupSymbol(proto.output_type(), method->full_name()); + if (output_type.IsNull()) { + AddNotDefinedError(method->full_name(), proto, + DescriptorPool::ErrorCollector::OUTPUT_TYPE, + proto.output_type()); + } else if (output_type.type != Symbol::MESSAGE) { + AddError(method->full_name(), proto, + DescriptorPool::ErrorCollector::OUTPUT_TYPE, + "\"" + proto.output_type() + "\" is not a message type."); + } else { + method->output_type_ = output_type.descriptor; + } +} + +void DescriptorBuilder::CrossLinkMapKey( + FieldDescriptor* field, + const FieldDescriptorProto& proto) { + if (!field->is_repeated()) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map type is only allowed for repeated fields."); + return; + } + + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map type is only allowed for fields with a message type."); + return; + } + + const Descriptor* item_type = field->message_type(); + if (item_type == NULL) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Could not find field type."); + return; + } + + // Find the field in item_type named by "experimental_map_key" + const string& key_name = proto.options().experimental_map_key(); + const Symbol key_symbol = LookupSymbol( + key_name, + // We append ".key_name" to the containing type's name since + // LookupSymbol() searches for peers of the supplied name, not + // children of the supplied name. + item_type->full_name() + "." + key_name); + + if (key_symbol.IsNull() || key_symbol.field_descriptor->is_extension()) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Could not find field named \"" + key_name + "\" in type \"" + + item_type->full_name() + "\"."); + return; + } + const FieldDescriptor* key_field = key_symbol.field_descriptor; + + if (key_field->is_repeated()) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map_key must not name a repeated field."); + return; + } + + if (key_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map key must name a scalar or string field."); + return; + } + + field->experimental_map_key_ = key_field; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h new file mode 100644 index 00000000..2bba4c38 --- /dev/null +++ b/src/google/protobuf/descriptor.h @@ -0,0 +1,1173 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains classes which describe a type of protocol message. +// You can use a message's descriptor to learn at runtime what fields +// it contains and what the types of those fields are. The Message +// interface also allows you to dynamically access and modify individual +// fields by passing the FieldDescriptor of the field you are interested +// in. +// +// Most users will not care about descriptors, because they will write +// code specific to certain protocol types and will simply use the classes +// generated by the protocol compiler directly. Advanced users who want +// to operate on arbitrary types (not known at compile time) may want to +// read descriptors in order to learn about the contents of a message. +// A very small number of users will want to construct their own +// Descriptors, either because they are implementing Message manually or +// because they are writing something like the protocol compiler. +// +// For an example of how you might use descriptors, see the code example +// at the top of message.h. + +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_H__ +#define GOOGLE_PROTOBUF_DESCRIPTOR_H__ + +#include +#include + + +namespace google { +namespace protobuf { + +// Defined in this file. +class Descriptor; +class FieldDescriptor; +class EnumDescriptor; +class EnumValueDescriptor; +class ServiceDescriptor; +class MethodDescriptor; +class FileDescriptor; +class DescriptorDatabase; +class DescriptorPool; + +// Defined in descriptor.proto +class DescriptorProto; +class FieldDescriptorProto; +class EnumDescriptorProto; +class EnumValueDescriptorProto; +class ServiceDescriptorProto; +class MethodDescriptorProto; +class FileDescriptorProto; +class MessageOptions; +class FieldOptions; +class EnumOptions; +class EnumValueOptions; +class ServiceOptions; +class MethodOptions; +class FileOptions; + +// Defined in message.h +class Message; + +// Defined in descriptor.cc +class DescriptorBuilder; + +// Describes a type of protocol message, or a particular group within a +// message. To obtain the Descriptor for a given message object, call +// Message::GetDescriptor(). Generated message classes also have a +// static method called descriptor() which returns the type's descriptor. +// Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT Descriptor { + public: + // The name of the message type, not including its scope. + const string& name() const; + + // The fully-qualified name of the message type, scope delimited by + // periods. For example, message type "Foo" which is declared in package + // "bar" has full name "bar.Foo". If a type "Baz" is nested within + // Foo, Baz's full_name is "bar.Foo.Baz". To get only the part that + // comes after the last '.', use name(). + const string& full_name() const; + + // Index of this descriptor within the file or containing type's message + // type array. + int index() const; + + // The .proto file in which this message type was defined. Never NULL. + const FileDescriptor* file() const; + + // If this Descriptor describes a nested type, this returns the type + // in which it is nested. Otherwise, returns NULL. + const Descriptor* containing_type() const; + + // Get options for this message type. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in the message definition. + // The exact set of known options is defined by MessageOptions in + // google/protobuf/descriptor.proto. + const MessageOptions& options() const; + + // Write the contents of this Descriptor into the given DescriptorProto. + // The target DescriptorProto must be clear before calling this; if it + // isn't, the result may be garbage. + void CopyTo(DescriptorProto* proto) const; + + // Write the contents of this decriptor in a human-readable form. Output + // will be suitable for re-parsing. + string DebugString() const; + + // Field stuff ----------------------------------------------------- + + // The number of fields in this message type. + int field_count() const; + // Gets a field by index, where 0 <= index < field_count(). + const FieldDescriptor* field(int index) const; + + // Looks up a field by declared tag number. Returns NULL if no such field + // exists. + const FieldDescriptor* FindFieldByNumber(int number) const; + // Looks up a field by name. Returns NULL if no such field exists. + const FieldDescriptor* FindFieldByName(const string& name) const; + + // Nested type stuff ----------------------------------------------- + + // The number of nested types in this message type. + int nested_type_count() const; + // Gets a nested type by index, where 0 <= index < nested_type_count(). + const Descriptor* nested_type(int index) const; + + // Looks up a nested type by name. Returns NULL if no such nested type + // exists. + const Descriptor* FindNestedTypeByName(const string& name) const; + + // Enum stuff ------------------------------------------------------ + + // The number of enum types in this message type. + int enum_type_count() const; + // Gets an enum type by index, where 0 <= index < enum_type_count(). + const EnumDescriptor* enum_type(int index) const; + + // Looks up an enum type by name. Returns NULL if no such enum type exists. + const EnumDescriptor* FindEnumTypeByName(const string& name) const; + + // Looks up an enum value by name, among all enum types in this message. + // Returns NULL if no such value exists. + const EnumValueDescriptor* FindEnumValueByName(const string& name) const; + + // Extensions ------------------------------------------------------ + + // A range of field numbers which are designated for third-party + // extensions. + struct ExtensionRange { + int start; // inclusive + int end; // exclusive + }; + + // The number of extension ranges in this message type. + int extension_range_count() const; + // Gets an extension range by index, where 0 <= index < + // extension_range_count(). + const ExtensionRange* extension_range(int index) const; + + // Returns true if the number is in one of the extension ranges. + bool IsExtensionNumber(int number) const; + + // The number of extensions -- extending *other* messages -- that were + // defined nested within this message type's scope. + int extension_count() const; + // Get an extension by index, where 0 <= index < extension_count(). + const FieldDescriptor* extension(int index) const; + + // Looks up a named extension (which extends some *other* message type) + // defined within this message type's scope. + const FieldDescriptor* FindExtensionByName(const string& name) const; + + private: + // Internal version of DebugString; controls the level of indenting for + // correct depth + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + const Descriptor* containing_type_; + const MessageOptions* options_; + int field_count_; + FieldDescriptor* fields_; + int nested_type_count_; + Descriptor* nested_types_; + int enum_type_count_; + EnumDescriptor* enum_types_; + int extension_range_count_; + ExtensionRange* extension_ranges_; + int extension_count_; + FieldDescriptor* extensions_; + + // Must be constructed using DescriptorPool. + Descriptor() {} + friend class DescriptorBuilder; + friend class EnumDescriptor; + friend class FieldDescriptor; + friend class FileDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Descriptor); +}; + +// Describes a single field of a message. To get the descriptor for a given +// field, first get the Descriptor for the message in which it is defined, +// then call Descriptor::FindFieldByName(). To get a FieldDescriptor for +// an extension, do one of the following: +// - Get the Descriptor or FileDescriptor for its containing scope, then +// call Descriptor::FindExtensionByName() or +// FileDescriptor::FindExtensionByName(). +// - Given a DescriptorPool, call DescriptorPool::FindExtensionByNumber(). +// - Given a Message::Reflection for a message object, call +// Message::Reflection::FindKnownExtensionByName() or +// Message::Reflection::FindKnownExtensionByNumber(). +// Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT FieldDescriptor { + public: + // Identifies a field type. 0 is reserved for errors. The order is weird + // for historical reasons. Types 12 and up are new in proto2. + enum Type { + TYPE_DOUBLE = 1, // double, exactly eight bytes on the wire. + TYPE_FLOAT = 2, // float, exactly four bytes on the wire. + TYPE_INT64 = 3, // int64, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. + TYPE_UINT64 = 4, // uint64, varint on the wire. + TYPE_INT32 = 5, // int32, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. + TYPE_FIXED64 = 6, // uint64, exactly eight bytes on the wire. + TYPE_FIXED32 = 7, // uint32, exactly four bytes on the wire. + TYPE_BOOL = 8, // bool, varint on the wire. + TYPE_STRING = 9, // UTF-8 text. + TYPE_GROUP = 10, // Tag-delimited message. Deprecated. + TYPE_MESSAGE = 11, // Length-delimited message. + + TYPE_BYTES = 12, // Arbitrary byte array. + TYPE_UINT32 = 13, // uint32, varint on the wire + TYPE_ENUM = 14, // Enum, varint on the wire + TYPE_SFIXED32 = 15, // int32, exactly four bytes on the wire + TYPE_SFIXED64 = 16, // int64, exactly eight bytes on the wire + TYPE_SINT32 = 17, // int32, ZigZag-encoded varint on the wire + TYPE_SINT64 = 18, // int64, ZigZag-encoded varint on the wire + + MAX_TYPE = 18, // Constant useful for defining lookup tables + // indexed by Type. + }; + + // Specifies the C++ data type used to represent the field. There is a + // fixed mapping from Type to CppType where each Type maps to exactly one + // CppType. 0 is reserved for errors. + enum CppType { + CPPTYPE_INT32 = 1, // TYPE_INT32, TYPE_SINT32, TYPE_SFIXED32 + CPPTYPE_INT64 = 2, // TYPE_INT64, TYPE_SINT64, TYPE_SFIXED64 + CPPTYPE_UINT32 = 3, // TYPE_UINT32, TYPE_FIXED32 + CPPTYPE_UINT64 = 4, // TYPE_UINT64, TYPE_FIXED64 + CPPTYPE_DOUBLE = 5, // TYPE_DOUBLE + CPPTYPE_FLOAT = 6, // TYPE_FLOAT + CPPTYPE_BOOL = 7, // TYPE_BOOL + CPPTYPE_ENUM = 8, // TYPE_ENUM + CPPTYPE_STRING = 9, // TYPE_STRING, TYPE_BYTES + CPPTYPE_MESSAGE = 10, // TYPE_MESSAGE, TYPE_GROUP + + MAX_CPPTYPE = 10, // Constant useful for defining lookup tables + // indexed by CppType. + }; + + // Identifies whether the field is optional, required, or repeated. 0 is + // reserved for errors. + enum Label { + LABEL_OPTIONAL = 1, // optional + LABEL_REQUIRED = 2, // required + LABEL_REPEATED = 3, // repeated + + MAX_LABEL = 3, // Constant useful for defining lookup tables + // indexed by Label. + }; + + // Valid field numbers are positive integers up to kMaxNumber. + static const int kMaxNumber = (1 << 29) - 1; + + // First field number reserved for the protocol buffer library implementation. + // Users may not declare fields that use reserved numbers. + static const int kFirstReservedNumber = 19000; + // Last field number reserved for the protocol buffer library implementation. + // Users may not declare fields that use reserved numbers. + static const int kLastReservedNumber = 19999; + + const string& name() const; // Name of this field within the message. + const string& full_name() const; // Fully-qualified name of the field. + const FileDescriptor* file() const;// File in which this field was defined. + bool is_extension() const; // Is this an extension field? + int number() const; // Declared tag number. + + Type type() const; // Declared type of this field. + CppType cpp_type() const; // C++ type of this field. + Label label() const; // optional/required/repeated + + bool is_required() const; // shorthand for label() == LABEL_REQUIRED + bool is_optional() const; // shorthand for label() == LABEL_OPTIONAL + bool is_repeated() const; // shorthand for label() == LABEL_REPEATED + + // Index of this field within the message's field array, or the file or + // extension scope's extensions array. + int index() const; + + // Does this field have an explicitly-declared default value? + bool has_default_value() const; + + // Get the field default value if cpp_type() == CPPTYPE_INT32. If no + // explicit default was defined, the default is 0. + int32 default_value_int32() const; + // Get the field default value if cpp_type() == CPPTYPE_INT64. If no + // explicit default was defined, the default is 0. + int64 default_value_int64() const; + // Get the field default value if cpp_type() == CPPTYPE_UINT32. If no + // explicit default was defined, the default is 0. + uint32 default_value_uint32() const; + // Get the field default value if cpp_type() == CPPTYPE_UINT64. If no + // explicit default was defined, the default is 0. + uint64 default_value_uint64() const; + // Get the field default value if cpp_type() == CPPTYPE_FLOAT. If no + // explicit default was defined, the default is 0.0. + float default_value_float() const; + // Get the field default value if cpp_type() == CPPTYPE_DOUBLE. If no + // explicit default was defined, the default is 0.0. + double default_value_double() const; + // Get the field default value if cpp_type() == CPPTYPE_BOOL. If no + // explicit default was defined, the default is false. + bool default_value_bool() const; + // Get the field default value if cpp_type() == CPPTYPE_ENUM. If no + // explicit default was defined, the default is the first value defined + // in the enum type (all enum types are required to have at least one value). + // This never returns NULL. + const EnumValueDescriptor* default_value_enum() const; + // Get the field default value if cpp_type() == CPPTYPE_STRING. If no + // explicit default was defined, the default is the empty string. + const string& default_value_string() const; + + // The Descriptor for the message of which this is a field. For extensions, + // this is the extended type. Never NULL. + const Descriptor* containing_type() const; + + // An extension may be declared within the scope of another message. If this + // field is an extension (is_extension() is true), then extension_scope() + // returns that message, or NULL if the extension was declared at global + // scope. If this is not an extension, extension_scope() is undefined (may + // assert-fail). + const Descriptor* extension_scope() const; + + // If type is TYPE_MESSAGE or TYPE_GROUP, returns a descriptor for the + // message or the group type. Otherwise, undefined. + const Descriptor* message_type() const; + // If type is TYPE_ENUM, returns a descriptor for the enum. Otherwise, + // undefined. + const EnumDescriptor* enum_type() const; + + // EXPERIMENTAL; DO NOT USE. + // If this field is a map field, experimental_map_key() is the field + // that is the key for this map. + // experimental_map_key()->containing_type() is the same as message_type(). + const FieldDescriptor* experimental_map_key() const; + + // Get the FieldOptions for this field. This includes things listed in + // square brackets after the field definition. E.g., the field: + // optional string text = 1 [ctype=CORD]; + // has the "ctype" option set. FieldOptions is actually a protocol message, + // which makes it easier to extend. + const FieldOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(FieldDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + // formats the default value appropriately and returns it as a string. + // Must have a default value to call this. If quote_string_type is true, then + // types of CPPTYPE_STRING whill be surrounded by quotes and CEscaped. + string DefaultValueAsString(bool quote_string_type) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + int number_; + Type type_; + Label label_; + bool is_extension_; + const Descriptor* containing_type_; + const Descriptor* extension_scope_; + const Descriptor* message_type_; + const EnumDescriptor* enum_type_; + const FieldDescriptor* experimental_map_key_; + const FieldOptions* options_; + + bool has_default_value_; + union { + int32 default_value_int32_; + int64 default_value_int64_; + uint32 default_value_uint32_; + uint64 default_value_uint64_; + float default_value_float_; + double default_value_double_; + bool default_value_bool_; + + const EnumValueDescriptor* default_value_enum_; + const string* default_value_string_; + }; + + static const CppType kTypeToCppTypeMap[MAX_TYPE + 1]; + + static const char * const kTypeToName[MAX_TYPE + 1]; + + static const char * const kLabelToName[MAX_LABEL + 1]; + + // Must be constructed using DescriptorPool. + FieldDescriptor() {} + friend class DescriptorBuilder; + friend class FileDescriptor; + friend class Descriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldDescriptor); +}; + +// Describes an enum type defined in a .proto file. To get the EnumDescriptor +// for a generated enum type, call TypeName_descriptor(). Use DescriptorPool +// to construct your own descriptors. +class LIBPROTOBUF_EXPORT EnumDescriptor { + public: + // The name of this enum type in the containing scope. + const string& name() const; + + // The fully-qualified name of the enum type, scope delimited by periods. + const string& full_name() const; + + // Index of this enum within the file or containing message's enum array. + int index() const; + + // The .proto file in which this enum type was defined. Never NULL. + const FileDescriptor* file() const; + + // The number of values for this EnumDescriptor. Guaranteed to be greater + // than zero. + int value_count() const; + // Gets a value by index, where 0 <= index < value_count(). + const EnumValueDescriptor* value(int index) const; + + // Looks up a value by name. Returns NULL if no such value exists. + const EnumValueDescriptor* FindValueByName(const string& name) const; + // Looks up a value by number. Returns NULL if no such value exists. If + // multiple values have this number, the first one defined is returned. + const EnumValueDescriptor* FindValueByNumber(int number) const; + + // If this enum type is nested in a message type, this is that message type. + // Otherwise, NULL. + const Descriptor* containing_type() const; + + // Get options for this enum type. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in the enum definition. + // The exact set of known options is defined by EnumOptions in + // google/protobuf/descriptor.proto. + const EnumOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(EnumDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + int value_count_; + EnumValueDescriptor* values_; + const Descriptor* containing_type_; + const EnumOptions* options_; + + // Must be constructed using DescriptorPool. + EnumDescriptor() {} + friend class DescriptorBuilder; + friend class Descriptor; + friend class EnumValueDescriptor; + friend class FileDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumDescriptor); +}; + +// Describes an individual enum constant of a particular type. To get the +// EnumValueDescriptor for a given enum value, first get the EnumDescriptor +// for its type, then use EnumDescriptor::FindValueByName() or +// EnumDescriptor::FindValueByNumber(). Use DescriptorPool to construct +// your own descriptors. +class LIBPROTOBUF_EXPORT EnumValueDescriptor { + public: + const string& name() const; // Name of this enum constant. + int index() const; // Index within the enums's Descriptor. + int number() const; // Numeric value of this enum constant. + + // The full_name of an enum value is a sibling symbol of the enum type. + // e.g. the full name of FieldDescriptorProto::TYPE_INT32 is actually + // "google.protobuf.FieldDescriptorProto.TYPE_INT32", NOT + // "google.protobuf.FieldDescriptorProto.Type.TYPE_INT32". This is to conform + // with C++ scoping rules for enums. + const string& full_name() const; + + // The type of this value. Never NULL. + const EnumDescriptor* type() const; + + // Get options for this enum value. These are specified in the .proto + // file by adding text like "[foo = 1234]" after an enum value definition. + // The exact set of known options is defined by EnumValueOptions in + // google/protobuf/descriptor.proto. + const EnumValueOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(EnumValueDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + int number_; + const EnumDescriptor* type_; + const EnumValueOptions* options_; + + // Must be constructed using DescriptorPool. + EnumValueDescriptor() {} + friend class DescriptorBuilder; + friend class EnumDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumValueDescriptor); +}; + +// Describes an RPC service. To get the ServiceDescriptor for a service, +// call Service::GetDescriptor(). Generated service classes also have a +// static method called descriptor() which returns the type's +// ServiceDescriptor. Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT ServiceDescriptor { + public: + // The name of the service, not including its containing scope. + const string& name() const; + // The fully-qualified name of the service, scope delimited by periods. + const string& full_name() const; + // Index of this service within the file's services array. + int index() const; + + // The .proto file in which this service was defined. Never NULL. + const FileDescriptor* file() const; + + // Get options for this service type. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in the service definition. + // The exact set of known options is defined by ServiceOptions in + // google/protobuf/descriptor.proto. + const ServiceOptions& options() const; + + // The number of methods this service defines. + int method_count() const; + // Gets a MethodDescriptor by index, where 0 <= index < method_count(). + const MethodDescriptor* method(int index) const; + + // Look up a MethodDescriptor by name. + const MethodDescriptor* FindMethodByName(const string& name) const; + + // See Descriptor::CopyTo(). + void CopyTo(ServiceDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(string *contents) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + const ServiceOptions* options_; + int method_count_; + MethodDescriptor* methods_; + + // Must be constructed using DescriptorPool. + ServiceDescriptor() {} + friend class DescriptorBuilder; + friend class FileDescriptor; + friend class MethodDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceDescriptor); +}; + +// Describes an individual service method. To obtain a MethodDescriptor given +// a service, first get its ServiceDescriptor, then call +// ServiceDescriptor::FindMethodByName(). Use DescriptorPool to construct your +// own descriptors. +class LIBPROTOBUF_EXPORT MethodDescriptor { + public: + // Name of this method, not including containing scope. + const string& name() const; + // The fully-qualified name of the method, scope delimited by periods. + const string& full_name() const; + // Index within the service's Descriptor. + int index() const; + + // Gets the service to which this method belongs. Never NULL. + const ServiceDescriptor* service() const; + + // Gets the type of protocol message which this method accepts as input. + const Descriptor* input_type() const; + // Gets the type of protocol message which this message produces as output. + const Descriptor* output_type() const; + + // Get options for this method. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in curly-braces after + // a method declaration. The exact set of known options is defined by + // MethodOptions in google/protobuf/descriptor.proto. + const MethodOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(MethodDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + const ServiceDescriptor* service_; + const Descriptor* input_type_; + const Descriptor* output_type_; + const MethodOptions* options_; + + // Must be constructed using DescriptorPool. + MethodDescriptor() {} + friend class DescriptorBuilder; + friend class ServiceDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MethodDescriptor); +}; + +// Describes a whole .proto file. To get the FileDescriptor for a compiled-in +// file, get the descriptor for something defined in that file and call +// descriptor->file(). Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT FileDescriptor { + public: + // The filename, relative to the source tree. + // e.g. "google/protobuf/descriptor.proto" + const string& name() const; + + // The package, e.g. "google.protobuf.compiler". + const string& package() const; + + // The DescriptorPool in which this FileDescriptor and all its contents were + // allocated. Never NULL. + const DescriptorPool* pool() const; + + // The number of files imported by this one. + int dependency_count() const; + // Gets an imported file by index, where 0 <= index < dependency_count(). + const FileDescriptor* dependency(int index) const; + + // Number of top-level message types defined in this file. (This does not + // include nested types.) + int message_type_count() const; + // Gets a top-level message type, where 0 <= index < message_type_count(). + const Descriptor* message_type(int index) const; + + // Number of top-level enum types defined in this file. (This does not + // include nested types.) + int enum_type_count() const; + // Gets a top-level enum type, where 0 <= index < enum_type_count(). + const EnumDescriptor* enum_type(int index) const; + + // Number of services defined in this file. + int service_count() const; + // Gets a service, where 0 <= index < service_count(). + const ServiceDescriptor* service(int index) const; + + // Number of extensions defined at file scope. (This does not include + // extensions nested within message types.) + int extension_count() const; + // Gets an extension's descriptor, where 0 <= index < extension_count(). + const FieldDescriptor* extension(int index) const; + + // Get options for this file. These are specified in the .proto + // file by placing lines like "option foo = 1234;" at the top level, outside + // of any other definitions. The exact set of known options is defined by + // FileOptions in google/protobuf/descriptor.proto. + const FileOptions& options() const; + + // Find a top-level message type by name. Returns NULL if not found. + const Descriptor* FindMessageTypeByName(const string& name) const; + // Find a top-level enum type by name. Returns NULL if not found. + const EnumDescriptor* FindEnumTypeByName(const string& name) const; + // Find an enum value defined in any top-level enum by name. Returns NULL if + // not found. + const EnumValueDescriptor* FindEnumValueByName(const string& name) const; + // Find a service definition by name. Returns NULL if not found. + const ServiceDescriptor* FindServiceByName(const string& name) const; + // Find a top-level extension definition by name. Returns NULL if not found. + const FieldDescriptor* FindExtensionByName(const string& name) const; + + // See Descriptor::CopyTo(). + void CopyTo(FileDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + const string* name_; + const string* package_; + const DescriptorPool* pool_; + int dependency_count_; + const FileDescriptor** dependencies_; + int message_type_count_; + Descriptor* message_types_; + int enum_type_count_; + EnumDescriptor* enum_types_; + int service_count_; + ServiceDescriptor* services_; + int extension_count_; + FieldDescriptor* extensions_; + const FileOptions* options_; + + FileDescriptor() {} + friend class DescriptorBuilder; + friend class Descriptor; + friend class FieldDescriptor; + friend class EnumDescriptor; + friend class ServiceDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileDescriptor); +}; + +// =================================================================== + +// Used to construct descriptors. +// +// Normally you won't want to build your own descriptors. Message classes +// constructed by the protocol compiler will provide them for you. However, +// if you are implementing Message on your own, or if you are writing a +// program which can operate on totally arbitrary types and needs to load +// them from some sort of database, you might need to. +// +// Since Descriptors are composed of a whole lot of cross-linked bits of +// data that would be a pain to put together manually, the +// DescriptorPool class is provided to make the process easier. It can +// take a FileDescriptorProto (defined in descriptor.proto), validate it, +// and convert it to a set of nicely cross-linked Descriptors. +// +// DescriptorPool also helps with memory management. Descriptors are +// composed of many objects containing static data and pointers to each +// other. In all likelihood, when it comes time to delete this data, +// you'll want to delete it all at once. In fact, it is not uncommon to +// have a whole pool of descriptors all cross-linked with each other which +// you wish to delete all at once. This class represents such a pool, and +// handles the memory management for you. +// +// You can also search for descriptors within a DescriptorPool by name, and +// extensions by number. +class LIBPROTOBUF_EXPORT DescriptorPool { + public: + // Create a normal, empty DescriptorPool. + DescriptorPool(); + + // Constructs a DescriptorPool that, when it can't find something among the + // descriptors already in the pool, looks for it in the given + // DescriptorDatabase. + // Notes: + // - If a DescriptorPool is constructed this way, its BuildFile*() methods + // must not be called (they will assert-fail). The only way to populate + // the pool with descriptors is to call the Find*By*() methods. + // - The Find*By*() methods may block the calling thread if the + // DescriptorDatabase blocks. This in turn means that parsing messages + // may block if they need to look up extensions. + // - The Find*By*() methods will use mutexes for thread-safety, thus making + // them slower even when they don't have to fall back to the database. + // In fact, even the Find*By*() methods of descriptor objects owned by + // this pool will be slower, since they will have to obtain locks too. + // - An ErrorCollector may optionally be given to collect validation errors + // in files loaded from the database. If not given, errors will be printed + // to GOOGLE_LOG(ERROR). Remember that files are built on-demand, so this + // ErrorCollector may be called from any thread that calls one of the + // Find*By*() methods. + class ErrorCollector; + explicit DescriptorPool(DescriptorDatabase* fallback_database, + ErrorCollector* error_collector = NULL); + + ~DescriptorPool(); + + // Get a pointer to the generated pool. Generated protocol message classes + // which are compiled into the binary will allocate their descriptors in + // this pool. Do not add your own descriptors to this pool. + static const DescriptorPool* generated_pool(); + + // Find a FileDescriptor in the pool by file name. Returns NULL if not + // found. + const FileDescriptor* FindFileByName(const string& name) const; + + // Find the FileDescriptor in the pool which defines the given symbol. + // If any of the Find*ByName() methods below would succeed, then this is + // equivalent to calling that method and calling the result's file() method. + // Otherwise this returns NULL. + const FileDescriptor* FindFileContainingSymbol( + const string& symbol_name) const; + + // Looking up descriptors ------------------------------------------ + // These find descriptors by fully-qualified name. These will find both + // top-level descriptors and nested descriptors. They return NULL if not + // found. + + const Descriptor* FindMessageTypeByName(const string& name) const; + const FieldDescriptor* FindFieldByName(const string& name) const; + const FieldDescriptor* FindExtensionByName(const string& name) const; + const EnumDescriptor* FindEnumTypeByName(const string& name) const; + const EnumValueDescriptor* FindEnumValueByName(const string& name) const; + const ServiceDescriptor* FindServiceByName(const string& name) const; + const MethodDescriptor* FindMethodByName(const string& name) const; + + // Finds an extension of the given type by number. The extendee must be + // a member of this DescriptorPool or one of its underlays. + const FieldDescriptor* FindExtensionByNumber(const Descriptor* extendee, + int number) const; + + // Building descriptors -------------------------------------------- + + // When converting a FileDescriptorProto to a FileDescriptor, various + // errors might be detected in the input. The caller may handle these + // programmatically by implementing an ErrorCollector. + class LIBPROTOBUF_EXPORT ErrorCollector { + public: + inline ErrorCollector() {} + virtual ~ErrorCollector(); + + // These constants specify what exact part of the construct is broken. + // This is useful e.g. for mapping the error back to an exact location + // in a .proto file. + enum ErrorLocation { + NAME, // the symbol name, or the package name for files + NUMBER, // field or extension range number + TYPE, // field type + EXTENDEE, // field extendee + DEFAULT_VALUE, // field default value + INPUT_TYPE, // method input type + OUTPUT_TYPE, // method output type + OTHER // some other problem + }; + + // Reports an error in the FileDescriptorProto. + virtual void AddError( + const string& filename, // File name in which the error occurred. + const string& element_name, // Full name of the erroneous element. + const Message* descriptor, // Descriptor of the erroneous element. + ErrorLocation location, // One of the location constants, above. + const string& message // Human-readable error message. + ) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector); + }; + + // Convert the FileDescriptorProto to real descriptors and place them in + // this DescriptorPool. All dependencies of the file must already be in + // the pool. Returns the resulting FileDescriptor, or NULL if there were + // problems with the input (e.g. the message was invalid, or dependencies + // were missing). Details about the errors are written to GOOGLE_LOG(ERROR). + const FileDescriptor* BuildFile(const FileDescriptorProto& proto); + + // Same as BuildFile() except errors are sent to the given ErrorCollector. + const FileDescriptor* BuildFileCollectingErrors( + const FileDescriptorProto& proto, + ErrorCollector* error_collector); + + // Internal stuff -------------------------------------------------- + // These methods MUST NOT be called from outside the proto2 library. + // These methods may contain hidden pitfalls and may be removed in a + // future library version. + + // DEPRECATED: Use of underlays can lead to many subtle gotchas. Instead, + // try to formulate what you want to do in terms of DescriptorDatabases. + // This constructor will be removed soon. + // + // Create a DescriptorPool which is overlaid on top of some other pool. + // If you search for a descriptor in the overlay and it is not found, the + // underlay will be searched as a backup. If the underlay has its own + // underlay, that will be searched next, and so on. This also means that + // files built in the overlay will be cross-linked with the underlay's + // descriptors if necessary. The underlay remains property of the caller; + // it must remain valid for the lifetime of the newly-constructed pool. + // + // Example: Say you want to parse a .proto file at runtime in order to use + // its type with a DynamicMessage. Say this .proto file has dependencies, + // but you know that all the dependencies will be things that are already + // compiled into the binary. For ease of use, you'd like to load the types + // right out of generated_pool() rather than have to parse redundant copies + // of all these .protos and runtime. But, you don't want to add the parsed + // types directly into generated_pool(): this is not allowed, and would be + // bad design anyway. So, instead, you could use generated_pool() as an + // underlay for a new DescriptorPool in which you add only the new file. + explicit DescriptorPool(const DescriptorPool* underlay); + + // Called by generated classes at init time. Do NOT call this in your + // own code! + const FileDescriptor* InternalBuildGeneratedFile( + const void* data, int size); + + // For internal use only: Gets a non-const pointer to the generated pool. + // This is called at static-initialization time only, so thread-safety is + // not a concern. If both an underlay and a fallback database are present, + // the fallback database takes precedence. + static DescriptorPool* internal_generated_pool(); + + // For internal use only: Changes the behavior of BuildFile() such that it + // allows the file to make reference to message types declared in other files + // which it did not officially declare as dependencies. + void InternalDontEnforceDependencies(); + + // For internal use only. + void internal_set_underlay(const DescriptorPool* underlay) { + underlay_ = underlay; + } + + private: + friend class Descriptor; + friend class FieldDescriptor; + friend class EnumDescriptor; + friend class ServiceDescriptor; + friend class FileDescriptor; + friend class DescriptorBuilder; + + // Tries to find something in the fallback database and link in the + // corresponding proto file. Returns true if successful, in which case + // the caller should search for the thing again. These are declared + // const because they are called by (semantically) const methods. + bool TryFindFileInFallbackDatabase(const string& name) const; + bool TryFindSymbolInFallbackDatabase(const string& name) const; + bool TryFindExtensionInFallbackDatabase(const Descriptor* containing_type, + int field_number) const; + + // Like BuildFile() but called internally when the file has been loaded from + // fallback_database_. Declared const because it is called by (semantically) + // const methods. + const FileDescriptor* BuildFileFromDatabase( + const FileDescriptorProto& proto) const; + + // If fallback_database_ is NULL, this is NULL. Otherwise, this is a mutex + // which must be locked while accessing tables_. + Mutex* mutex_; + + // See constructor. + DescriptorDatabase* fallback_database_; + ErrorCollector* default_error_collector_; + const DescriptorPool* underlay_; + + // This class contains a lot of hash maps with complicated types that + // we'd like to keep out of the header. + class Tables; + scoped_ptr tables_; + + bool enforce_dependencies_; + + // See InternalBuildGeneratedFile(). + const void* last_internal_build_generated_file_call_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPool); +}; + +// inline methods ==================================================== + +// These macros makes this repetitive code more readable. +#define PROTOBUF_DEFINE_ACCESSOR(CLASS, FIELD, TYPE) \ + inline TYPE CLASS::FIELD() const { return FIELD##_; } + +// Strings fields are stored as pointers but returned as const references. +#define PROTOBUF_DEFINE_STRING_ACCESSOR(CLASS, FIELD) \ + inline const string& CLASS::FIELD() const { return *FIELD##_; } + +// Arrays take an index parameter, obviously. +#define PROTOBUF_DEFINE_ARRAY_ACCESSOR(CLASS, FIELD, TYPE) \ + inline TYPE CLASS::FIELD(int index) const { return FIELD##s_ + index; } + +#define PROTOBUF_DEFINE_OPTIONS_ACCESSOR(CLASS, TYPE) \ + inline const TYPE& CLASS::options() const { return *options_; } + +PROTOBUF_DEFINE_STRING_ACCESSOR(Descriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(Descriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, containing_type, const Descriptor*) + +PROTOBUF_DEFINE_ACCESSOR(Descriptor, field_count, int) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, nested_type_count, int) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, enum_type_count, int) + +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, field, const FieldDescriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, nested_type, const Descriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, enum_type, const EnumDescriptor*) + +PROTOBUF_DEFINE_ACCESSOR(Descriptor, extension_range_count, int) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, extension_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension_range, + const Descriptor::ExtensionRange*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension, + const FieldDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, is_extension, bool) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, type, FieldDescriptor::Type) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, label, FieldDescriptor::Label) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, containing_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, extension_scope, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, message_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, enum_type, const EnumDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, experimental_map_key, + const FieldDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FieldDescriptor, FieldOptions); +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, has_default_value, bool) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_int32 , int32 ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_int64 , int64 ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_uint32, uint32) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_uint64, uint64) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_float , float ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_double, double) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_bool , bool ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_enum, + const EnumValueDescriptor*) +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, default_value_string) + +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, containing_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, value_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(EnumDescriptor, value, + const EnumValueDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumDescriptor, EnumOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumValueDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumValueDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, number, int) +PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, type, const EnumDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumValueDescriptor, EnumValueOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(ServiceDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(ServiceDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, method_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(ServiceDescriptor, method, + const MethodDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(ServiceDescriptor, ServiceOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(MethodDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(MethodDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, service, const ServiceDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, input_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, output_type, const Descriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(MethodDescriptor, MethodOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(FileDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(FileDescriptor, package) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, pool, const DescriptorPool*) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, dependency_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, message_type_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, enum_type_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, service_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, extension_count, int) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FileDescriptor, FileOptions); + +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, message_type, const Descriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, enum_type, const EnumDescriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, service, + const ServiceDescriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, extension, + const FieldDescriptor*) + +#undef PROTOBUF_DEFINE_ACCESSOR +#undef PROTOBUF_DEFINE_STRING_ACCESSOR +#undef PROTOBUF_DEFINE_ARRAY_ACCESSOR + +// A few accessors differ from the macros... + +inline bool FieldDescriptor::is_required() const { + return label() == LABEL_REQUIRED; +} + +inline bool FieldDescriptor::is_optional() const { + return label() == LABEL_OPTIONAL; +} + +inline bool FieldDescriptor::is_repeated() const { + return label() == LABEL_REPEATED; +} + +// To save space, index() is computed by looking at the descriptor's position +// in the parent's array of children. +inline int FieldDescriptor::index() const { + if (!is_extension_) { + return this - containing_type_->fields_; + } else if (extension_scope_ != NULL) { + return this - extension_scope_->extensions_; + } else { + return this - file_->extensions_; + } +} + +inline int Descriptor::index() const { + if (containing_type_ == NULL) { + return this - file_->message_types_; + } else { + return this - containing_type_->nested_types_; + } +} + +inline int EnumDescriptor::index() const { + if (containing_type_ == NULL) { + return this - file_->enum_types_; + } else { + return this - containing_type_->enum_types_; + } +} + +inline int EnumValueDescriptor::index() const { + return this - type_->values_; +} + +inline int ServiceDescriptor::index() const { + return this - file_->services_; +} + +inline int MethodDescriptor::index() const { + return this - service_->methods_; +} + +inline FieldDescriptor::CppType FieldDescriptor::cpp_type() const { + return kTypeToCppTypeMap[type_]; +} + +inline const FileDescriptor* FileDescriptor::dependency(int index) const { + return dependencies_[index]; +} + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_DESCRIPTOR_H__ diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc new file mode 100644 index 00000000..836d141e --- /dev/null +++ b/src/google/protobuf/descriptor.pb.cc @@ -0,0 +1,3935 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#include "google/protobuf/descriptor.pb.h" +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace { + +const ::google::protobuf::Descriptor* FileDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::Descriptor* DescriptorProto_descriptor_ = NULL; +const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange_descriptor_ = NULL; +const ::google::protobuf::Descriptor* FieldDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Type_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Label_descriptor_ = NULL; +const ::google::protobuf::Descriptor* EnumDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::Descriptor* EnumValueDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::Descriptor* ServiceDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::Descriptor* MethodDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::Descriptor* FileOptions_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* FileOptions_OptimizeMode_descriptor_ = NULL; +const ::google::protobuf::Descriptor* MessageOptions_descriptor_ = NULL; +const ::google::protobuf::Descriptor* FieldOptions_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor_ = NULL; +const ::google::protobuf::Descriptor* EnumOptions_descriptor_ = NULL; +const ::google::protobuf::Descriptor* EnumValueOptions_descriptor_ = NULL; +const ::google::protobuf::Descriptor* ServiceOptions_descriptor_ = NULL; +const ::google::protobuf::Descriptor* MethodOptions_descriptor_ = NULL; + +} // namespace + + +void proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + ::google::protobuf::DescriptorPool* pool = + ::google::protobuf::DescriptorPool::internal_generated_pool(); + + const ::google::protobuf::FileDescriptor* file = pool->InternalBuildGeneratedFile( + "\n google/protobuf/descriptor.proto\022\017goog" + "le.protobuf\"\334\002\n\023FileDescriptorProto\022\014\n\004n" + "ame\030\001 \001(\t\022\017\n\007package\030\002 \001(\t\022\022\n\ndependency" + "\030\003 \003(\t\0226\n\014message_type\030\004 \003(\0132 .google.pr" + "otobuf.DescriptorProto\0227\n\tenum_type\030\005 \003(" + "\0132$.google.protobuf.EnumDescriptorProto\022" + "8\n\007service\030\006 \003(\0132\'.google.protobuf.Servi" + "ceDescriptorProto\0228\n\textension\030\007 \003(\0132%.g" + "oogle.protobuf.FieldDescriptorProto\022-\n\007o" + "ptions\030\010 \001(\0132\034.google.protobuf.FileOptio" + "ns\"\251\003\n\017DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005" + "field\030\002 \003(\0132%.google.protobuf.FieldDescr" + "iptorProto\0228\n\textension\030\006 \003(\0132%.google.p" + "rotobuf.FieldDescriptorProto\0225\n\013nested_t" + "ype\030\003 \003(\0132 .google.protobuf.DescriptorPr" + "oto\0227\n\tenum_type\030\004 \003(\0132$.google.protobuf" + ".EnumDescriptorProto\022H\n\017extension_range\030" + "\005 \003(\0132/.google.protobuf.DescriptorProto." + "ExtensionRange\0220\n\007options\030\007 \001(\0132\037.google" + ".protobuf.MessageOptions\032,\n\016ExtensionRan" + "ge\022\r\n\005start\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\"\224\005\n\024Field" + "DescriptorProto\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030" + "\003 \001(\005\022:\n\005label\030\004 \001(\0162+.google.protobuf.F" + "ieldDescriptorProto.Label\0228\n\004type\030\005 \001(\0162" + "*.google.protobuf.FieldDescriptorProto.T" + "ype\022\021\n\ttype_name\030\006 \001(\t\022\020\n\010extendee\030\002 \001(\t" + "\022\025\n\rdefault_value\030\007 \001(\t\022.\n\007options\030\010 \001(\013" + "2\035.google.protobuf.FieldOptions\"\266\002\n\004Type" + "\022\017\n\013TYPE_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYP" + "E_INT64\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32" + "\020\005\022\020\n\014TYPE_FIXED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r" + "\n\tTYPE_BOOL\020\010\022\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_G" + "ROUP\020\n\022\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014" + "\022\017\n\013TYPE_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE" + "_SFIXED32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_S" + "INT32\020\021\022\017\n\013TYPE_SINT64\020\022\"C\n\005Label\022\022\n\016LAB" + "EL_OPTIONAL\020\001\022\022\n\016LABEL_REQUIRED\020\002\022\022\n\016LAB" + "EL_REPEATED\020\003\"\214\001\n\023EnumDescriptorProto\022\014\n" + "\004name\030\001 \001(\t\0228\n\005value\030\002 \003(\0132).google.prot" + "obuf.EnumValueDescriptorProto\022-\n\007options" + "\030\003 \001(\0132\034.google.protobuf.EnumOptions\"l\n\030" + "EnumValueDescriptorProto\022\014\n\004name\030\001 \001(\t\022\016" + "\n\006number\030\002 \001(\005\0222\n\007options\030\003 \001(\0132!.google" + ".protobuf.EnumValueOptions\"\220\001\n\026ServiceDe" + "scriptorProto\022\014\n\004name\030\001 \001(\t\0226\n\006method\030\002 " + "\003(\0132&.google.protobuf.MethodDescriptorPr" + "oto\0220\n\007options\030\003 \001(\0132\037.google.protobuf.S" + "erviceOptions\"\177\n\025MethodDescriptorProto\022\014" + "\n\004name\030\001 \001(\t\022\022\n\ninput_type\030\002 \001(\t\022\023\n\013outp" + "ut_type\030\003 \001(\t\022/\n\007options\030\004 \001(\0132\036.google." + "protobuf.MethodOptions\"\333\001\n\013FileOptions\022\024" + "\n\014java_package\030\001 \001(\t\022\034\n\024java_outer_class" + "name\030\010 \001(\t\022\"\n\023java_multiple_files\030\n \001(\010:" + "\005false\022J\n\014optimize_for\030\t \001(\0162).google.pr" + "otobuf.FileOptions.OptimizeMode:\tCODE_SI" + "ZE\"(\n\014OptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCODE_SI" + "ZE\020\002\"8\n\016MessageOptions\022&\n\027message_set_wi" + "re_format\030\001 \001(\010:\005false\"\205\001\n\014FieldOptions\022" + "2\n\005ctype\030\001 \001(\0162#.google.protobuf.FieldOp" + "tions.CType\022\034\n\024experimental_map_key\030\t \001(" + "\t\"#\n\005CType\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"\r" + "\n\013EnumOptions\"\022\n\020EnumValueOptions\"\020\n\016Ser" + "viceOptions\"\017\n\rMethodOptionsB)\n\023com.goog" + "le.protobufB\020DescriptorProtosH\001", 2551); + FileDescriptorProto_descriptor_ = file->message_type(0); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FileDescriptorProto_descriptor_, &FileDescriptorProto::default_instance()); + DescriptorProto_descriptor_ = file->message_type(1); + DescriptorProto_ExtensionRange_descriptor_ = DescriptorProto_descriptor_->nested_type(0); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DescriptorProto_ExtensionRange_descriptor_, &DescriptorProto_ExtensionRange::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DescriptorProto_descriptor_, &DescriptorProto::default_instance()); + FieldDescriptorProto_descriptor_ = file->message_type(2); + FieldDescriptorProto_Type_descriptor_ = FieldDescriptorProto_descriptor_->enum_type(0); + FieldDescriptorProto_Label_descriptor_ = FieldDescriptorProto_descriptor_->enum_type(1); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FieldDescriptorProto_descriptor_, &FieldDescriptorProto::default_instance()); + EnumDescriptorProto_descriptor_ = file->message_type(3); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumDescriptorProto_descriptor_, &EnumDescriptorProto::default_instance()); + EnumValueDescriptorProto_descriptor_ = file->message_type(4); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumValueDescriptorProto_descriptor_, &EnumValueDescriptorProto::default_instance()); + ServiceDescriptorProto_descriptor_ = file->message_type(5); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + ServiceDescriptorProto_descriptor_, &ServiceDescriptorProto::default_instance()); + MethodDescriptorProto_descriptor_ = file->message_type(6); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + MethodDescriptorProto_descriptor_, &MethodDescriptorProto::default_instance()); + FileOptions_descriptor_ = file->message_type(7); + FileOptions_OptimizeMode_descriptor_ = FileOptions_descriptor_->enum_type(0); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FileOptions_descriptor_, &FileOptions::default_instance()); + MessageOptions_descriptor_ = file->message_type(8); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + MessageOptions_descriptor_, &MessageOptions::default_instance()); + FieldOptions_descriptor_ = file->message_type(9); + FieldOptions_CType_descriptor_ = FieldOptions_descriptor_->enum_type(0); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FieldOptions_descriptor_, &FieldOptions::default_instance()); + EnumOptions_descriptor_ = file->message_type(10); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumOptions_descriptor_, &EnumOptions::default_instance()); + EnumValueOptions_descriptor_ = file->message_type(11); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumValueOptions_descriptor_, &EnumValueOptions::default_instance()); + ServiceOptions_descriptor_ = file->message_type(12); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + ServiceOptions_descriptor_, &ServiceOptions::default_instance()); + MethodOptions_descriptor_ = file->message_type(13); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + MethodOptions_descriptor_, &MethodOptions::default_instance()); +} + +// Force BuildDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_google_2fprotobuf_2fdescriptor_2eproto { + StaticDescriptorInitializer_google_2fprotobuf_2fdescriptor_2eproto() { + proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + } +} static_descriptor_initializer_google_2fprotobuf_2fdescriptor_2eproto_; + + +// =================================================================== + +const FileDescriptorProto FileDescriptorProto::default_instance_; + +const ::std::string FileDescriptorProto::_default_name_; +const ::std::string FileDescriptorProto::_default_package_; + + + + + + +const int FileDescriptorProto::_offsets_[8] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, package_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, dependency_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, message_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, enum_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, service_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, extension_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, options_), +}; + +FileDescriptorProto::FileDescriptorProto() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + package_(const_cast< ::std::string*>(&_default_package_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::FileOptions*>(&::google::protobuf::FileOptions::default_instance()); + } +} + +FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + package_(const_cast< ::std::string*>(&_default_package_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FileDescriptorProto::~FileDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (package_ != &_default_package_) { + delete package_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* FileDescriptorProto::descriptor() { + if (FileDescriptorProto_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FileDescriptorProto_descriptor_; +} + +FileDescriptorProto* FileDescriptorProto::New() const { + return new FileDescriptorProto; +} + +void FileDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(1)) { + if (package_ != &_default_package_) { + package_->clear(); + } + } + if (_has_bit(7)) { + if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); + } + } + dependency_.Clear(); + message_type_.Clear(); + enum_type_.Clear(); + service_.Clear(); + extension_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FileDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_package; + break; + } + + // optional string package = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_package: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_package())); + if (input->ExpectTag(26)) goto parse_dependency; + break; + } + + // repeated string dependency = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_dependency: + DO_(::google::protobuf::internal::WireFormat::ReadString( + input, add_dependency())); + if (input->ExpectTag(26)) goto parse_dependency; + if (input->ExpectTag(34)) goto parse_message_type; + break; + } + + // repeated .google.protobuf.DescriptorProto message_type = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_message_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_message_type())); + if (input->ExpectTag(34)) goto parse_message_type; + if (input->ExpectTag(42)) goto parse_enum_type; + break; + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + case 5: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_enum_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_enum_type())); + if (input->ExpectTag(42)) goto parse_enum_type; + if (input->ExpectTag(50)) goto parse_service; + break; + } + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + case 6: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_service: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_service())); + if (input->ExpectTag(50)) goto parse_service; + if (input->ExpectTag(58)) goto parse_extension; + break; + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + case 7: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extension: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_extension())); + if (input->ExpectTag(58)) goto parse_extension; + if (input->ExpectTag(66)) goto parse_options; + break; + } + + // optional .google.protobuf.FileOptions options = 8; + case 8: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FileDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional string package = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(2, this->package(), output)); + } + + // repeated string dependency = 3; + for (int i = 0; i < dependency_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteString(3, this->dependency(i), output)); + } + + // repeated .google.protobuf.DescriptorProto message_type = 4; + for (int i = 0; i < message_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->message_type(i), output)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + for (int i = 0; i < enum_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(5, this->enum_type(i), output)); + } + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + for (int i = 0; i < service_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(6, this->service(i), output)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + for (int i = 0; i < extension_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(7, this->extension(i), output)); + } + + // optional .google.protobuf.FileOptions options = 8; + if (_has_bit(7)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(8, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FileDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional string package = 2; + if (has_package()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->package()); + } + + // optional .google.protobuf.FileOptions options = 8; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated string dependency = 3; + total_size += 1 * dependency_size(); + for (int i = 0; i < dependency_size(); i++) { + total_size += ::google::protobuf::internal::WireFormat::StringSize( + this->dependency(i)); + } + + // repeated .google.protobuf.DescriptorProto message_type = 4; + total_size += 1 * message_type_size(); + for (int i = 0; i < message_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->message_type(i)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + total_size += 1 * enum_type_size(); + for (int i = 0; i < enum_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->enum_type(i)); + } + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + total_size += 1 * service_size(); + for (int i = 0; i < service_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->service(i)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + total_size += 1 * extension_size(); + for (int i = 0; i < extension_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->extension(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FileDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FileDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void FileDescriptorProto::MergeFrom(const FileDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + dependency_.MergeFrom(from.dependency_); + message_type_.MergeFrom(from.message_type_); + enum_type_.MergeFrom(from.enum_type_); + service_.MergeFrom(from.service_); + extension_.MergeFrom(from.extension_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_package(from.package()); + } + if (from._has_bit(7)) { + mutable_options()->::google::protobuf::FileOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FileDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FileDescriptorProto::CopyFrom(const FileDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FileDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FileDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +FileDescriptorProto::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* FileDescriptorProto::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const DescriptorProto_ExtensionRange DescriptorProto_ExtensionRange::default_instance_; + + + +const int DescriptorProto_ExtensionRange::_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, start_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, end_), +}; + +DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + start_(0), + end_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange(const DescriptorProto_ExtensionRange& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + start_(0), + end_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +DescriptorProto_ExtensionRange::~DescriptorProto_ExtensionRange() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange::descriptor() { + if (DescriptorProto_ExtensionRange_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return DescriptorProto_ExtensionRange_descriptor_; +} + +DescriptorProto_ExtensionRange* DescriptorProto_ExtensionRange::New() const { + return new DescriptorProto_ExtensionRange; +} + +void DescriptorProto_ExtensionRange::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + start_ = 0; + end_ = 0; + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool DescriptorProto_ExtensionRange::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional int32 start = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &start_)); + _set_bit(0); + if (input->ExpectTag(16)) goto parse_end; + break; + } + + // optional int32 end = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_end: + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &end_)); + _set_bit(1); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool DescriptorProto_ExtensionRange::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional int32 start = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(1, this->start(), output)); + } + + // optional int32 end = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(2, this->end(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int DescriptorProto_ExtensionRange::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional int32 start = 1; + if (has_start()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->start()); + } + + // optional int32 end = 2; + if (has_end()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->end()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void DescriptorProto_ExtensionRange::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const DescriptorProto_ExtensionRange* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void DescriptorProto_ExtensionRange::MergeFrom(const DescriptorProto_ExtensionRange& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_start(from.start()); + } + if (from._has_bit(1)) { + set_end(from.end()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void DescriptorProto_ExtensionRange::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DescriptorProto_ExtensionRange::CopyFrom(const DescriptorProto_ExtensionRange& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool DescriptorProto_ExtensionRange::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +DescriptorProto_ExtensionRange::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* DescriptorProto_ExtensionRange::GetReflection() { + return &_reflection_; +} + +// ------------------------------------------------------------------- + +const DescriptorProto DescriptorProto::default_instance_; + +const ::std::string DescriptorProto::_default_name_; + + + + + + +const int DescriptorProto::_offsets_[7] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, field_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, nested_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, enum_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_range_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, options_), +}; + +DescriptorProto::DescriptorProto() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::MessageOptions*>(&::google::protobuf::MessageOptions::default_instance()); + } +} + +DescriptorProto::DescriptorProto(const DescriptorProto& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +DescriptorProto::~DescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* DescriptorProto::descriptor() { + if (DescriptorProto_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return DescriptorProto_descriptor_; +} + +DescriptorProto* DescriptorProto::New() const { + return new DescriptorProto; +} + +void DescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(6)) { + if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); + } + } + field_.Clear(); + extension_.Clear(); + nested_type_.Clear(); + enum_type_.Clear(); + extension_range_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool DescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_field; + break; + } + + // repeated .google.protobuf.FieldDescriptorProto field = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_field: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_field())); + if (input->ExpectTag(18)) goto parse_field; + if (input->ExpectTag(26)) goto parse_nested_type; + break; + } + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_nested_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_nested_type())); + if (input->ExpectTag(26)) goto parse_nested_type; + if (input->ExpectTag(34)) goto parse_enum_type; + break; + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_enum_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_enum_type())); + if (input->ExpectTag(34)) goto parse_enum_type; + if (input->ExpectTag(42)) goto parse_extension_range; + break; + } + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + case 5: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extension_range: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_extension_range())); + if (input->ExpectTag(42)) goto parse_extension_range; + if (input->ExpectTag(50)) goto parse_extension; + break; + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + case 6: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extension: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_extension())); + if (input->ExpectTag(50)) goto parse_extension; + if (input->ExpectTag(58)) goto parse_options; + break; + } + + // optional .google.protobuf.MessageOptions options = 7; + case 7: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool DescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // repeated .google.protobuf.FieldDescriptorProto field = 2; + for (int i = 0; i < field_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->field(i), output)); + } + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + for (int i = 0; i < nested_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->nested_type(i), output)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + for (int i = 0; i < enum_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->enum_type(i), output)); + } + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + for (int i = 0; i < extension_range_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(5, this->extension_range(i), output)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + for (int i = 0; i < extension_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(6, this->extension(i), output)); + } + + // optional .google.protobuf.MessageOptions options = 7; + if (_has_bit(6)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(7, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int DescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional .google.protobuf.MessageOptions options = 7; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated .google.protobuf.FieldDescriptorProto field = 2; + total_size += 1 * field_size(); + for (int i = 0; i < field_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->field(i)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + total_size += 1 * extension_size(); + for (int i = 0; i < extension_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->extension(i)); + } + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + total_size += 1 * nested_type_size(); + for (int i = 0; i < nested_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->nested_type(i)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + total_size += 1 * enum_type_size(); + for (int i = 0; i < enum_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->enum_type(i)); + } + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + total_size += 1 * extension_range_size(); + for (int i = 0; i < extension_range_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->extension_range(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void DescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const DescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void DescriptorProto::MergeFrom(const DescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + field_.MergeFrom(from.field_); + extension_.MergeFrom(from.extension_); + nested_type_.MergeFrom(from.nested_type_); + enum_type_.MergeFrom(from.enum_type_); + extension_range_.MergeFrom(from.extension_range_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(6)) { + mutable_options()->::google::protobuf::MessageOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void DescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DescriptorProto::CopyFrom(const DescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool DescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* DescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +DescriptorProto::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* DescriptorProto::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Type_descriptor() { + if (FieldDescriptorProto_Type_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldDescriptorProto_Type_descriptor_; +} +bool FieldDescriptorProto_Type_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_DOUBLE; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FLOAT; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_INT64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_UINT64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_INT32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FIXED64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FIXED32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_BOOL; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_STRING; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_GROUP; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_MESSAGE; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_BYTES; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_UINT32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_ENUM; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SFIXED32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SFIXED64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SINT32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SINT64; +const FieldDescriptorProto_Type FieldDescriptorProto::Type_MIN; +const FieldDescriptorProto_Type FieldDescriptorProto::Type_MAX; +#endif // _MSC_VER +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Label_descriptor() { + if (FieldDescriptorProto_Label_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldDescriptorProto_Label_descriptor_; +} +bool FieldDescriptorProto_Label_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldDescriptorProto_Label FieldDescriptorProto::LABEL_OPTIONAL; +const FieldDescriptorProto_Label FieldDescriptorProto::LABEL_REQUIRED; +const FieldDescriptorProto_Label FieldDescriptorProto::LABEL_REPEATED; +const FieldDescriptorProto_Label FieldDescriptorProto::Label_MIN; +const FieldDescriptorProto_Label FieldDescriptorProto::Label_MAX; +#endif // _MSC_VER +const FieldDescriptorProto FieldDescriptorProto::default_instance_; + +const ::std::string FieldDescriptorProto::_default_name_; + + + +const ::std::string FieldDescriptorProto::_default_type_name_; +const ::std::string FieldDescriptorProto::_default_extendee_; +const ::std::string FieldDescriptorProto::_default_default_value_; + +const int FieldDescriptorProto::_offsets_[8] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, number_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, label_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, type_name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, extendee_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, default_value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, options_), +}; + +FieldDescriptorProto::FieldDescriptorProto() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + label_(1), + type_(1), + type_name_(const_cast< ::std::string*>(&_default_type_name_)), + extendee_(const_cast< ::std::string*>(&_default_extendee_)), + default_value_(const_cast< ::std::string*>(&_default_default_value_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::FieldOptions*>(&::google::protobuf::FieldOptions::default_instance()); + } +} + +FieldDescriptorProto::FieldDescriptorProto(const FieldDescriptorProto& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + label_(1), + type_(1), + type_name_(const_cast< ::std::string*>(&_default_type_name_)), + extendee_(const_cast< ::std::string*>(&_default_extendee_)), + default_value_(const_cast< ::std::string*>(&_default_default_value_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FieldDescriptorProto::~FieldDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (type_name_ != &_default_type_name_) { + delete type_name_; + } + if (extendee_ != &_default_extendee_) { + delete extendee_; + } + if (default_value_ != &_default_default_value_) { + delete default_value_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* FieldDescriptorProto::descriptor() { + if (FieldDescriptorProto_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldDescriptorProto_descriptor_; +} + +FieldDescriptorProto* FieldDescriptorProto::New() const { + return new FieldDescriptorProto; +} + +void FieldDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + number_ = 0; + label_ = 1; + type_ = 1; + if (_has_bit(4)) { + if (type_name_ != &_default_type_name_) { + type_name_->clear(); + } + } + if (_has_bit(5)) { + if (extendee_ != &_default_extendee_) { + extendee_->clear(); + } + } + if (_has_bit(6)) { + if (default_value_ != &_default_default_value_) { + default_value_->clear(); + } + } + if (_has_bit(7)) { + if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FieldDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_extendee; + break; + } + + // optional string extendee = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extendee: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_extendee())); + if (input->ExpectTag(24)) goto parse_number; + break; + } + + // optional int32 number = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_number: + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &number_)); + _set_bit(1); + if (input->ExpectTag(32)) goto parse_label; + break; + } + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_label: + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FieldDescriptorProto_Label_IsValid(value)) { + set_label(static_cast< ::google::protobuf::FieldDescriptorProto_Label >(value)); + } else { + mutable_unknown_fields()->AddField(4)->add_varint(value); + } + if (input->ExpectTag(40)) goto parse_type; + break; + } + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + case 5: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_type: + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FieldDescriptorProto_Type_IsValid(value)) { + set_type(static_cast< ::google::protobuf::FieldDescriptorProto_Type >(value)); + } else { + mutable_unknown_fields()->AddField(5)->add_varint(value); + } + if (input->ExpectTag(50)) goto parse_type_name; + break; + } + + // optional string type_name = 6; + case 6: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_type_name: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_type_name())); + if (input->ExpectTag(58)) goto parse_default_value; + break; + } + + // optional string default_value = 7; + case 7: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_default_value: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_default_value())); + if (input->ExpectTag(66)) goto parse_options; + break; + } + + // optional .google.protobuf.FieldOptions options = 8; + case 8: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FieldDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional string extendee = 2; + if (_has_bit(5)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(2, this->extendee(), output)); + } + + // optional int32 number = 3; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(3, this->number(), output)); + } + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(4, this->label(), output)); + } + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + if (_has_bit(3)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(5, this->type(), output)); + } + + // optional string type_name = 6; + if (_has_bit(4)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(6, this->type_name(), output)); + } + + // optional string default_value = 7; + if (_has_bit(6)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(7, this->default_value(), output)); + } + + // optional .google.protobuf.FieldOptions options = 8; + if (_has_bit(7)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(8, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FieldDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional int32 number = 3; + if (has_number()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->number()); + } + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + if (has_label()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->label()); + } + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->type()); + } + + // optional string type_name = 6; + if (has_type_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->type_name()); + } + + // optional string extendee = 2; + if (has_extendee()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->extendee()); + } + + // optional string default_value = 7; + if (has_default_value()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->default_value()); + } + + // optional .google.protobuf.FieldOptions options = 8; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FieldDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FieldDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void FieldDescriptorProto::MergeFrom(const FieldDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_number(from.number()); + } + if (from._has_bit(2)) { + set_label(from.label()); + } + if (from._has_bit(3)) { + set_type(from.type()); + } + if (from._has_bit(4)) { + set_type_name(from.type_name()); + } + if (from._has_bit(5)) { + set_extendee(from.extendee()); + } + if (from._has_bit(6)) { + set_default_value(from.default_value()); + } + if (from._has_bit(7)) { + mutable_options()->::google::protobuf::FieldOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FieldDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FieldDescriptorProto::CopyFrom(const FieldDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FieldDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FieldDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +FieldDescriptorProto::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* FieldDescriptorProto::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const EnumDescriptorProto EnumDescriptorProto::default_instance_; + +const ::std::string EnumDescriptorProto::_default_name_; + + +const int EnumDescriptorProto::_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, options_), +}; + +EnumDescriptorProto::EnumDescriptorProto() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::EnumOptions*>(&::google::protobuf::EnumOptions::default_instance()); + } +} + +EnumDescriptorProto::EnumDescriptorProto(const EnumDescriptorProto& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumDescriptorProto::~EnumDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* EnumDescriptorProto::descriptor() { + if (EnumDescriptorProto_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumDescriptorProto_descriptor_; +} + +EnumDescriptorProto* EnumDescriptorProto::New() const { + return new EnumDescriptorProto; +} + +void EnumDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(2)) { + if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); + } + } + value_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_value; + break; + } + + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_value: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_value())); + if (input->ExpectTag(18)) goto parse_value; + if (input->ExpectTag(26)) goto parse_options; + break; + } + + // optional .google.protobuf.EnumOptions options = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool EnumDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + for (int i = 0; i < value_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->value(i), output)); + } + + // optional .google.protobuf.EnumOptions options = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional .google.protobuf.EnumOptions options = 3; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + total_size += 1 * value_size(); + for (int i = 0; i < value_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->value(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const EnumDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void EnumDescriptorProto::MergeFrom(const EnumDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + value_.MergeFrom(from.value_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(2)) { + mutable_options()->::google::protobuf::EnumOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumDescriptorProto::CopyFrom(const EnumDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +EnumDescriptorProto::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* EnumDescriptorProto::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const EnumValueDescriptorProto EnumValueDescriptorProto::default_instance_; + +const ::std::string EnumValueDescriptorProto::_default_name_; + + +const int EnumValueDescriptorProto::_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, number_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, options_), +}; + +EnumValueDescriptorProto::EnumValueDescriptorProto() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::EnumValueOptions*>(&::google::protobuf::EnumValueOptions::default_instance()); + } +} + +EnumValueDescriptorProto::EnumValueDescriptorProto(const EnumValueDescriptorProto& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumValueDescriptorProto::~EnumValueDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* EnumValueDescriptorProto::descriptor() { + if (EnumValueDescriptorProto_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumValueDescriptorProto_descriptor_; +} + +EnumValueDescriptorProto* EnumValueDescriptorProto::New() const { + return new EnumValueDescriptorProto; +} + +void EnumValueDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + number_ = 0; + if (_has_bit(2)) { + if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumValueDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(16)) goto parse_number; + break; + } + + // optional int32 number = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_number: + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &number_)); + _set_bit(1); + if (input->ExpectTag(26)) goto parse_options; + break; + } + + // optional .google.protobuf.EnumValueOptions options = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool EnumValueDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional int32 number = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(2, this->number(), output)); + } + + // optional .google.protobuf.EnumValueOptions options = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumValueDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional int32 number = 2; + if (has_number()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->number()); + } + + // optional .google.protobuf.EnumValueOptions options = 3; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumValueDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const EnumValueDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void EnumValueDescriptorProto::MergeFrom(const EnumValueDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_number(from.number()); + } + if (from._has_bit(2)) { + mutable_options()->::google::protobuf::EnumValueOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumValueDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumValueDescriptorProto::CopyFrom(const EnumValueDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumValueDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumValueDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +EnumValueDescriptorProto::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* EnumValueDescriptorProto::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const ServiceDescriptorProto ServiceDescriptorProto::default_instance_; + +const ::std::string ServiceDescriptorProto::_default_name_; + + +const int ServiceDescriptorProto::_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, method_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, options_), +}; + +ServiceDescriptorProto::ServiceDescriptorProto() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::ServiceOptions*>(&::google::protobuf::ServiceOptions::default_instance()); + } +} + +ServiceDescriptorProto::ServiceDescriptorProto(const ServiceDescriptorProto& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +ServiceDescriptorProto::~ServiceDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* ServiceDescriptorProto::descriptor() { + if (ServiceDescriptorProto_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return ServiceDescriptorProto_descriptor_; +} + +ServiceDescriptorProto* ServiceDescriptorProto::New() const { + return new ServiceDescriptorProto; +} + +void ServiceDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(2)) { + if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); + } + } + method_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool ServiceDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_method; + break; + } + + // repeated .google.protobuf.MethodDescriptorProto method = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_method: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_method())); + if (input->ExpectTag(18)) goto parse_method; + if (input->ExpectTag(26)) goto parse_options; + break; + } + + // optional .google.protobuf.ServiceOptions options = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool ServiceDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // repeated .google.protobuf.MethodDescriptorProto method = 2; + for (int i = 0; i < method_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->method(i), output)); + } + + // optional .google.protobuf.ServiceOptions options = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int ServiceDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional .google.protobuf.ServiceOptions options = 3; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated .google.protobuf.MethodDescriptorProto method = 2; + total_size += 1 * method_size(); + for (int i = 0; i < method_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->method(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void ServiceDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const ServiceDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void ServiceDescriptorProto::MergeFrom(const ServiceDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + method_.MergeFrom(from.method_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(2)) { + mutable_options()->::google::protobuf::ServiceOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void ServiceDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void ServiceDescriptorProto::CopyFrom(const ServiceDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ServiceDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* ServiceDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +ServiceDescriptorProto::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* ServiceDescriptorProto::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const MethodDescriptorProto MethodDescriptorProto::default_instance_; + +const ::std::string MethodDescriptorProto::_default_name_; +const ::std::string MethodDescriptorProto::_default_input_type_; +const ::std::string MethodDescriptorProto::_default_output_type_; + +const int MethodDescriptorProto::_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, input_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, output_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, options_), +}; + +MethodDescriptorProto::MethodDescriptorProto() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + input_type_(const_cast< ::std::string*>(&_default_input_type_)), + output_type_(const_cast< ::std::string*>(&_default_output_type_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::MethodOptions*>(&::google::protobuf::MethodOptions::default_instance()); + } +} + +MethodDescriptorProto::MethodDescriptorProto(const MethodDescriptorProto& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + input_type_(const_cast< ::std::string*>(&_default_input_type_)), + output_type_(const_cast< ::std::string*>(&_default_output_type_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +MethodDescriptorProto::~MethodDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (input_type_ != &_default_input_type_) { + delete input_type_; + } + if (output_type_ != &_default_output_type_) { + delete output_type_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* MethodDescriptorProto::descriptor() { + if (MethodDescriptorProto_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return MethodDescriptorProto_descriptor_; +} + +MethodDescriptorProto* MethodDescriptorProto::New() const { + return new MethodDescriptorProto; +} + +void MethodDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(1)) { + if (input_type_ != &_default_input_type_) { + input_type_->clear(); + } + } + if (_has_bit(2)) { + if (output_type_ != &_default_output_type_) { + output_type_->clear(); + } + } + if (_has_bit(3)) { + if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool MethodDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_input_type; + break; + } + + // optional string input_type = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_input_type: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_input_type())); + if (input->ExpectTag(26)) goto parse_output_type; + break; + } + + // optional string output_type = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_output_type: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_output_type())); + if (input->ExpectTag(34)) goto parse_options; + break; + } + + // optional .google.protobuf.MethodOptions options = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool MethodDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional string input_type = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(2, this->input_type(), output)); + } + + // optional string output_type = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(3, this->output_type(), output)); + } + + // optional .google.protobuf.MethodOptions options = 4; + if (_has_bit(3)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int MethodDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional string input_type = 2; + if (has_input_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->input_type()); + } + + // optional string output_type = 3; + if (has_output_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->output_type()); + } + + // optional .google.protobuf.MethodOptions options = 4; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void MethodDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const MethodDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void MethodDescriptorProto::MergeFrom(const MethodDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_input_type(from.input_type()); + } + if (from._has_bit(2)) { + set_output_type(from.output_type()); + } + if (from._has_bit(3)) { + mutable_options()->::google::protobuf::MethodOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void MethodDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void MethodDescriptorProto::CopyFrom(const MethodDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MethodDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* MethodDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +MethodDescriptorProto::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* MethodDescriptorProto::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* FileOptions_OptimizeMode_descriptor() { + if (FileOptions_OptimizeMode_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FileOptions_OptimizeMode_descriptor_; +} +bool FileOptions_OptimizeMode_IsValid(int value) { + switch(value) { + case 1: + case 2: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FileOptions_OptimizeMode FileOptions::SPEED; +const FileOptions_OptimizeMode FileOptions::CODE_SIZE; +const FileOptions_OptimizeMode FileOptions::OptimizeMode_MIN; +const FileOptions_OptimizeMode FileOptions::OptimizeMode_MAX; +#endif // _MSC_VER +const FileOptions FileOptions::default_instance_; + +const ::std::string FileOptions::_default_java_package_; +const ::std::string FileOptions::_default_java_outer_classname_; + + +const int FileOptions::_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_package_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_outer_classname_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_multiple_files_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, optimize_for_), +}; + +FileOptions::FileOptions() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + java_package_(const_cast< ::std::string*>(&_default_java_package_)), + java_outer_classname_(const_cast< ::std::string*>(&_default_java_outer_classname_)), + java_multiple_files_(false), + optimize_for_(2) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +FileOptions::FileOptions(const FileOptions& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + java_package_(const_cast< ::std::string*>(&_default_java_package_)), + java_outer_classname_(const_cast< ::std::string*>(&_default_java_outer_classname_)), + java_multiple_files_(false), + optimize_for_(2) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FileOptions::~FileOptions() { + if (java_package_ != &_default_java_package_) { + delete java_package_; + } + if (java_outer_classname_ != &_default_java_outer_classname_) { + delete java_outer_classname_; + } + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* FileOptions::descriptor() { + if (FileOptions_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FileOptions_descriptor_; +} + +FileOptions* FileOptions::New() const { + return new FileOptions; +} + +void FileOptions::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (java_package_ != &_default_java_package_) { + java_package_->clear(); + } + } + if (_has_bit(1)) { + if (java_outer_classname_ != &_default_java_outer_classname_) { + java_outer_classname_->clear(); + } + } + java_multiple_files_ = false; + optimize_for_ = 2; + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FileOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string java_package = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_java_package())); + if (input->ExpectTag(66)) goto parse_java_outer_classname; + break; + } + + // optional string java_outer_classname = 8; + case 8: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_java_outer_classname: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_java_outer_classname())); + if (input->ExpectTag(72)) goto parse_optimize_for; + break; + } + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + case 9: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_optimize_for: + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FileOptions_OptimizeMode_IsValid(value)) { + set_optimize_for(static_cast< ::google::protobuf::FileOptions_OptimizeMode >(value)); + } else { + mutable_unknown_fields()->AddField(9)->add_varint(value); + } + if (input->ExpectTag(80)) goto parse_java_multiple_files; + break; + } + + // optional bool java_multiple_files = 10 [default = false]; + case 10: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_java_multiple_files: + DO_(::google::protobuf::internal::WireFormat::ReadBool( + input, &java_multiple_files_)); + _set_bit(2); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FileOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string java_package = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->java_package(), output)); + } + + // optional string java_outer_classname = 8; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(8, this->java_outer_classname(), output)); + } + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + if (_has_bit(3)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(9, this->optimize_for(), output)); + } + + // optional bool java_multiple_files = 10 [default = false]; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteBool(10, this->java_multiple_files(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FileOptions::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string java_package = 1; + if (has_java_package()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->java_package()); + } + + // optional string java_outer_classname = 8; + if (has_java_outer_classname()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->java_outer_classname()); + } + + // optional bool java_multiple_files = 10 [default = false]; + if (has_java_multiple_files()) { + total_size += 1 + 1; + } + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + if (has_optimize_for()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->optimize_for()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FileOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FileOptions* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void FileOptions::MergeFrom(const FileOptions& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_java_package(from.java_package()); + } + if (from._has_bit(1)) { + set_java_outer_classname(from.java_outer_classname()); + } + if (from._has_bit(2)) { + set_java_multiple_files(from.java_multiple_files()); + } + if (from._has_bit(3)) { + set_optimize_for(from.optimize_for()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FileOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FileOptions::CopyFrom(const FileOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FileOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FileOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +FileOptions::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* FileOptions::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const MessageOptions MessageOptions::default_instance_; + + +const int MessageOptions::_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, message_set_wire_format_), +}; + +MessageOptions::MessageOptions() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + message_set_wire_format_(false) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +MessageOptions::MessageOptions(const MessageOptions& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + message_set_wire_format_(false) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +MessageOptions::~MessageOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* MessageOptions::descriptor() { + if (MessageOptions_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return MessageOptions_descriptor_; +} + +MessageOptions* MessageOptions::New() const { + return new MessageOptions; +} + +void MessageOptions::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + message_set_wire_format_ = false; + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool MessageOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional bool message_set_wire_format = 1 [default = false]; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadBool( + input, &message_set_wire_format_)); + _set_bit(0); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool MessageOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional bool message_set_wire_format = 1 [default = false]; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteBool(1, this->message_set_wire_format(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int MessageOptions::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional bool message_set_wire_format = 1 [default = false]; + if (has_message_set_wire_format()) { + total_size += 1 + 1; + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void MessageOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const MessageOptions* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void MessageOptions::MergeFrom(const MessageOptions& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_message_set_wire_format(from.message_set_wire_format()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void MessageOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void MessageOptions::CopyFrom(const MessageOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MessageOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* MessageOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +MessageOptions::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* MessageOptions::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor() { + if (FieldOptions_CType_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldOptions_CType_descriptor_; +} +bool FieldOptions_CType_IsValid(int value) { + switch(value) { + case 1: + case 2: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldOptions_CType FieldOptions::CORD; +const FieldOptions_CType FieldOptions::STRING_PIECE; +const FieldOptions_CType FieldOptions::CType_MIN; +const FieldOptions_CType FieldOptions::CType_MAX; +#endif // _MSC_VER +const FieldOptions FieldOptions::default_instance_; + + +const ::std::string FieldOptions::_default_experimental_map_key_; +const int FieldOptions::_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, ctype_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, experimental_map_key_), +}; + +FieldOptions::FieldOptions() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + ctype_(1), + experimental_map_key_(const_cast< ::std::string*>(&_default_experimental_map_key_)) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +FieldOptions::FieldOptions(const FieldOptions& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0), + ctype_(1), + experimental_map_key_(const_cast< ::std::string*>(&_default_experimental_map_key_)) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FieldOptions::~FieldOptions() { + if (experimental_map_key_ != &_default_experimental_map_key_) { + delete experimental_map_key_; + } + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* FieldOptions::descriptor() { + if (FieldOptions_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldOptions_descriptor_; +} + +FieldOptions* FieldOptions::New() const { + return new FieldOptions; +} + +void FieldOptions::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + ctype_ = 1; + if (_has_bit(1)) { + if (experimental_map_key_ != &_default_experimental_map_key_) { + experimental_map_key_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FieldOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional .google.protobuf.FieldOptions.CType ctype = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FieldOptions_CType_IsValid(value)) { + set_ctype(static_cast< ::google::protobuf::FieldOptions_CType >(value)); + } else { + mutable_unknown_fields()->AddField(1)->add_varint(value); + } + if (input->ExpectTag(74)) goto parse_experimental_map_key; + break; + } + + // optional string experimental_map_key = 9; + case 9: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_experimental_map_key: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_experimental_map_key())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FieldOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional .google.protobuf.FieldOptions.CType ctype = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(1, this->ctype(), output)); + } + + // optional string experimental_map_key = 9; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(9, this->experimental_map_key(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FieldOptions::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .google.protobuf.FieldOptions.CType ctype = 1; + if (has_ctype()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->ctype()); + } + + // optional string experimental_map_key = 9; + if (has_experimental_map_key()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->experimental_map_key()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FieldOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FieldOptions* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge( + descriptor(), *from.GetReflection(), &_reflection_); + } else { + MergeFrom(*source); + } +} + +void FieldOptions::MergeFrom(const FieldOptions& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_ctype(from.ctype()); + } + if (from._has_bit(1)) { + set_experimental_map_key(from.experimental_map_key()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FieldOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FieldOptions::CopyFrom(const FieldOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FieldOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FieldOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +FieldOptions::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* FieldOptions::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const EnumOptions EnumOptions::default_instance_; + +const int EnumOptions::_offsets_[1] = { +}; + +EnumOptions::EnumOptions() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +EnumOptions::EnumOptions(const EnumOptions& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumOptions::~EnumOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* EnumOptions::descriptor() { + if (EnumOptions_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumOptions_descriptor_; +} + +EnumOptions* EnumOptions::New() const { + return new EnumOptions; +} + +void EnumOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool EnumOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void EnumOptions::MergeFrom(const EnumOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumOptions::CopyFrom(const EnumOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +EnumOptions::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* EnumOptions::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const EnumValueOptions EnumValueOptions::default_instance_; + +const int EnumValueOptions::_offsets_[1] = { +}; + +EnumValueOptions::EnumValueOptions() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +EnumValueOptions::EnumValueOptions(const EnumValueOptions& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumValueOptions::~EnumValueOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* EnumValueOptions::descriptor() { + if (EnumValueOptions_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumValueOptions_descriptor_; +} + +EnumValueOptions* EnumValueOptions::New() const { + return new EnumValueOptions; +} + +void EnumValueOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumValueOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool EnumValueOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumValueOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumValueOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void EnumValueOptions::MergeFrom(const EnumValueOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumValueOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumValueOptions::CopyFrom(const EnumValueOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumValueOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumValueOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +EnumValueOptions::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* EnumValueOptions::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const ServiceOptions ServiceOptions::default_instance_; + +const int ServiceOptions::_offsets_[1] = { +}; + +ServiceOptions::ServiceOptions() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +ServiceOptions::ServiceOptions(const ServiceOptions& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +ServiceOptions::~ServiceOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* ServiceOptions::descriptor() { + if (ServiceOptions_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return ServiceOptions_descriptor_; +} + +ServiceOptions* ServiceOptions::New() const { + return new ServiceOptions; +} + +void ServiceOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool ServiceOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool ServiceOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int ServiceOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void ServiceOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void ServiceOptions::MergeFrom(const ServiceOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void ServiceOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void ServiceOptions::CopyFrom(const ServiceOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ServiceOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* ServiceOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +ServiceOptions::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* ServiceOptions::GetReflection() { + return &_reflection_; +} + +// =================================================================== + +const MethodOptions MethodOptions::default_instance_; + +const int MethodOptions::_offsets_[1] = { +}; + +MethodOptions::MethodOptions() + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +MethodOptions::MethodOptions(const MethodOptions& from) + : _reflection_(descriptor(), + this, &default_instance_, + _offsets_, _has_bits_, NULL), + _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +MethodOptions::~MethodOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* MethodOptions::descriptor() { + if (MethodOptions_descriptor_ == NULL) proto_BuildDescriptors_google_2fprotobuf_2fdescriptor_2eproto(); + return MethodOptions_descriptor_; +} + +MethodOptions* MethodOptions::New() const { + return new MethodOptions; +} + +void MethodOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool MethodOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool MethodOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int MethodOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void MethodOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void MethodOptions::MergeFrom(const MethodOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void MethodOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void MethodOptions::CopyFrom(const MethodOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MethodOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* MethodOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Message::Reflection* +MethodOptions::GetReflection() const { + return &_reflection_; +} + +::google::protobuf::Message::Reflection* MethodOptions::GetReflection() { + return &_reflection_; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h new file mode 100644 index 00000000..892f92d0 --- /dev/null +++ b/src/google/protobuf/descriptor.pb.h @@ -0,0 +1,2958 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#ifndef PROTOBUF_google_2fprotobuf_2fdescriptor_2eproto__INCLUDED +#define PROTOBUF_google_2fprotobuf_2fdescriptor_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2000000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2000001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include + +namespace google { +namespace protobuf { + +class FileDescriptorProto; +class DescriptorProto; +class DescriptorProto_ExtensionRange; +class FieldDescriptorProto; +class EnumDescriptorProto; +class EnumValueDescriptorProto; +class ServiceDescriptorProto; +class MethodDescriptorProto; +class FileOptions; +class MessageOptions; +class FieldOptions; +class EnumOptions; +class EnumValueOptions; +class ServiceOptions; +class MethodOptions; + +enum FieldDescriptorProto_Type { + FieldDescriptorProto_Type_TYPE_DOUBLE = 1, + FieldDescriptorProto_Type_TYPE_FLOAT = 2, + FieldDescriptorProto_Type_TYPE_INT64 = 3, + FieldDescriptorProto_Type_TYPE_UINT64 = 4, + FieldDescriptorProto_Type_TYPE_INT32 = 5, + FieldDescriptorProto_Type_TYPE_FIXED64 = 6, + FieldDescriptorProto_Type_TYPE_FIXED32 = 7, + FieldDescriptorProto_Type_TYPE_BOOL = 8, + FieldDescriptorProto_Type_TYPE_STRING = 9, + FieldDescriptorProto_Type_TYPE_GROUP = 10, + FieldDescriptorProto_Type_TYPE_MESSAGE = 11, + FieldDescriptorProto_Type_TYPE_BYTES = 12, + FieldDescriptorProto_Type_TYPE_UINT32 = 13, + FieldDescriptorProto_Type_TYPE_ENUM = 14, + FieldDescriptorProto_Type_TYPE_SFIXED32 = 15, + FieldDescriptorProto_Type_TYPE_SFIXED64 = 16, + FieldDescriptorProto_Type_TYPE_SINT32 = 17, + FieldDescriptorProto_Type_TYPE_SINT64 = 18, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Type_descriptor(); +LIBPROTOBUF_EXPORT bool FieldDescriptorProto_Type_IsValid(int value); +const FieldDescriptorProto_Type FieldDescriptorProto_Type_Type_MIN = FieldDescriptorProto_Type_TYPE_DOUBLE; +const FieldDescriptorProto_Type FieldDescriptorProto_Type_Type_MAX = FieldDescriptorProto_Type_TYPE_SINT64; + +enum FieldDescriptorProto_Label { + FieldDescriptorProto_Label_LABEL_OPTIONAL = 1, + FieldDescriptorProto_Label_LABEL_REQUIRED = 2, + FieldDescriptorProto_Label_LABEL_REPEATED = 3, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Label_descriptor(); +LIBPROTOBUF_EXPORT bool FieldDescriptorProto_Label_IsValid(int value); +const FieldDescriptorProto_Label FieldDescriptorProto_Label_Label_MIN = FieldDescriptorProto_Label_LABEL_OPTIONAL; +const FieldDescriptorProto_Label FieldDescriptorProto_Label_Label_MAX = FieldDescriptorProto_Label_LABEL_REPEATED; + +enum FileOptions_OptimizeMode { + FileOptions_OptimizeMode_SPEED = 1, + FileOptions_OptimizeMode_CODE_SIZE = 2, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FileOptions_OptimizeMode_descriptor(); +LIBPROTOBUF_EXPORT bool FileOptions_OptimizeMode_IsValid(int value); +const FileOptions_OptimizeMode FileOptions_OptimizeMode_OptimizeMode_MIN = FileOptions_OptimizeMode_SPEED; +const FileOptions_OptimizeMode FileOptions_OptimizeMode_OptimizeMode_MAX = FileOptions_OptimizeMode_CODE_SIZE; + +enum FieldOptions_CType { + FieldOptions_CType_CORD = 1, + FieldOptions_CType_STRING_PIECE = 2, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor(); +LIBPROTOBUF_EXPORT bool FieldOptions_CType_IsValid(int value); +const FieldOptions_CType FieldOptions_CType_CType_MIN = FieldOptions_CType_CORD; +const FieldOptions_CType FieldOptions_CType_CType_MAX = FieldOptions_CType_STRING_PIECE; + +// =================================================================== + +class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Message { + public: + FileDescriptorProto(); + virtual ~FileDescriptorProto(); + + FileDescriptorProto(const FileDescriptorProto& from); + + inline FileDescriptorProto& operator=(const FileDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const FileDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FileDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FileDescriptorProto& from); + void MergeFrom(const FileDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional string package = 2; + inline bool has_package() const; + inline void clear_package(); + inline const ::std::string& package() const; + inline void set_package(const ::std::string& value); + inline void set_package(const char* value); + inline ::std::string* mutable_package(); + + // repeated string dependency = 3; + inline int dependency_size() const; + inline void clear_dependency(); + inline const ::google::protobuf::RepeatedPtrField< ::std::string>& dependency() const; + inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_dependency(); + inline const ::std::string& dependency(int index) const; + inline ::std::string* mutable_dependency(int index); + inline void set_dependency(int index, const ::std::string& value); + inline void set_dependency(int index, const char* value); + inline ::std::string* add_dependency(); + inline void add_dependency(const ::std::string& value); + inline void add_dependency(const char* value); + + // repeated .google.protobuf.DescriptorProto message_type = 4; + inline int message_type_size() const; + inline void clear_message_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& message_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* mutable_message_type(); + inline const ::google::protobuf::DescriptorProto& message_type(int index) const; + inline ::google::protobuf::DescriptorProto* mutable_message_type(int index); + inline ::google::protobuf::DescriptorProto* add_message_type(); + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + inline int enum_type_size() const; + inline void clear_enum_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& enum_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* mutable_enum_type(); + inline const ::google::protobuf::EnumDescriptorProto& enum_type(int index) const; + inline ::google::protobuf::EnumDescriptorProto* mutable_enum_type(int index); + inline ::google::protobuf::EnumDescriptorProto* add_enum_type(); + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + inline int service_size() const; + inline void clear_service(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& service() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* mutable_service(); + inline const ::google::protobuf::ServiceDescriptorProto& service(int index) const; + inline ::google::protobuf::ServiceDescriptorProto* mutable_service(int index); + inline ::google::protobuf::ServiceDescriptorProto* add_service(); + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + inline int extension_size() const; + inline void clear_extension(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& extension() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_extension(); + inline const ::google::protobuf::FieldDescriptorProto& extension(int index) const; + inline ::google::protobuf::FieldDescriptorProto* mutable_extension(int index); + inline ::google::protobuf::FieldDescriptorProto* add_extension(); + + // optional .google.protobuf.FileOptions options = 8; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::FileOptions& options() const; + inline ::google::protobuf::FileOptions* mutable_options(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::std::string* package_; + static const ::std::string _default_package_; + ::google::protobuf::RepeatedPtrField< ::std::string> dependency_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto > message_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto > enum_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto > service_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > extension_; + ::google::protobuf::FileOptions* options_; + + static const FileDescriptorProto default_instance_; + static const int _offsets_[8]; + + ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT DescriptorProto_ExtensionRange : public ::google::protobuf::Message { + public: + DescriptorProto_ExtensionRange(); + virtual ~DescriptorProto_ExtensionRange(); + + DescriptorProto_ExtensionRange(const DescriptorProto_ExtensionRange& from); + + inline DescriptorProto_ExtensionRange& operator=(const DescriptorProto_ExtensionRange& from) { + CopyFrom(from); + return *this; + } + + inline static const DescriptorProto_ExtensionRange& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + DescriptorProto_ExtensionRange* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DescriptorProto_ExtensionRange& from); + void MergeFrom(const DescriptorProto_ExtensionRange& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional int32 start = 1; + inline bool has_start() const; + inline void clear_start(); + inline ::google::protobuf::int32 start() const; + inline void set_start(::google::protobuf::int32 value); + + // optional int32 end = 2; + inline bool has_end() const; + inline void clear_end(); + inline ::google::protobuf::int32 end() const; + inline void set_end(::google::protobuf::int32 value); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::google::protobuf::int32 start_; + ::google::protobuf::int32 end_; + + static const DescriptorProto_ExtensionRange default_instance_; + static const int _offsets_[2]; + + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { + public: + DescriptorProto(); + virtual ~DescriptorProto(); + + DescriptorProto(const DescriptorProto& from); + + inline DescriptorProto& operator=(const DescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const DescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + DescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DescriptorProto& from); + void MergeFrom(const DescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + typedef DescriptorProto_ExtensionRange ExtensionRange; + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // repeated .google.protobuf.FieldDescriptorProto field = 2; + inline int field_size() const; + inline void clear_field(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& field() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_field(); + inline const ::google::protobuf::FieldDescriptorProto& field(int index) const; + inline ::google::protobuf::FieldDescriptorProto* mutable_field(int index); + inline ::google::protobuf::FieldDescriptorProto* add_field(); + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + inline int extension_size() const; + inline void clear_extension(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& extension() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_extension(); + inline const ::google::protobuf::FieldDescriptorProto& extension(int index) const; + inline ::google::protobuf::FieldDescriptorProto* mutable_extension(int index); + inline ::google::protobuf::FieldDescriptorProto* add_extension(); + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + inline int nested_type_size() const; + inline void clear_nested_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& nested_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* mutable_nested_type(); + inline const ::google::protobuf::DescriptorProto& nested_type(int index) const; + inline ::google::protobuf::DescriptorProto* mutable_nested_type(int index); + inline ::google::protobuf::DescriptorProto* add_nested_type(); + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + inline int enum_type_size() const; + inline void clear_enum_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& enum_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* mutable_enum_type(); + inline const ::google::protobuf::EnumDescriptorProto& enum_type(int index) const; + inline ::google::protobuf::EnumDescriptorProto* mutable_enum_type(int index); + inline ::google::protobuf::EnumDescriptorProto* add_enum_type(); + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + inline int extension_range_size() const; + inline void clear_extension_range(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& extension_range() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* mutable_extension_range(); + inline const ::google::protobuf::DescriptorProto_ExtensionRange& extension_range(int index) const; + inline ::google::protobuf::DescriptorProto_ExtensionRange* mutable_extension_range(int index); + inline ::google::protobuf::DescriptorProto_ExtensionRange* add_extension_range(); + + // optional .google.protobuf.MessageOptions options = 7; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::MessageOptions& options() const; + inline ::google::protobuf::MessageOptions* mutable_options(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > field_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > extension_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto > nested_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto > enum_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange > extension_range_; + ::google::protobuf::MessageOptions* options_; + + static const DescriptorProto default_instance_; + static const int _offsets_[7]; + + ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Message { + public: + FieldDescriptorProto(); + virtual ~FieldDescriptorProto(); + + FieldDescriptorProto(const FieldDescriptorProto& from); + + inline FieldDescriptorProto& operator=(const FieldDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const FieldDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FieldDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FieldDescriptorProto& from); + void MergeFrom(const FieldDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + typedef FieldDescriptorProto_Type Type; + static const Type TYPE_DOUBLE = FieldDescriptorProto_Type_TYPE_DOUBLE; + static const Type TYPE_FLOAT = FieldDescriptorProto_Type_TYPE_FLOAT; + static const Type TYPE_INT64 = FieldDescriptorProto_Type_TYPE_INT64; + static const Type TYPE_UINT64 = FieldDescriptorProto_Type_TYPE_UINT64; + static const Type TYPE_INT32 = FieldDescriptorProto_Type_TYPE_INT32; + static const Type TYPE_FIXED64 = FieldDescriptorProto_Type_TYPE_FIXED64; + static const Type TYPE_FIXED32 = FieldDescriptorProto_Type_TYPE_FIXED32; + static const Type TYPE_BOOL = FieldDescriptorProto_Type_TYPE_BOOL; + static const Type TYPE_STRING = FieldDescriptorProto_Type_TYPE_STRING; + static const Type TYPE_GROUP = FieldDescriptorProto_Type_TYPE_GROUP; + static const Type TYPE_MESSAGE = FieldDescriptorProto_Type_TYPE_MESSAGE; + static const Type TYPE_BYTES = FieldDescriptorProto_Type_TYPE_BYTES; + static const Type TYPE_UINT32 = FieldDescriptorProto_Type_TYPE_UINT32; + static const Type TYPE_ENUM = FieldDescriptorProto_Type_TYPE_ENUM; + static const Type TYPE_SFIXED32 = FieldDescriptorProto_Type_TYPE_SFIXED32; + static const Type TYPE_SFIXED64 = FieldDescriptorProto_Type_TYPE_SFIXED64; + static const Type TYPE_SINT32 = FieldDescriptorProto_Type_TYPE_SINT32; + static const Type TYPE_SINT64 = FieldDescriptorProto_Type_TYPE_SINT64; + static inline const ::google::protobuf::EnumDescriptor* + Type_descriptor() { + return FieldDescriptorProto_Type_descriptor(); + } + static inline bool Type_IsValid(int value) { + return FieldDescriptorProto_Type_IsValid(value); + } + static const Type Type_MIN = + FieldDescriptorProto_Type_Type_MIN; + static const Type Type_MAX = + FieldDescriptorProto_Type_Type_MAX; + + typedef FieldDescriptorProto_Label Label; + static const Label LABEL_OPTIONAL = FieldDescriptorProto_Label_LABEL_OPTIONAL; + static const Label LABEL_REQUIRED = FieldDescriptorProto_Label_LABEL_REQUIRED; + static const Label LABEL_REPEATED = FieldDescriptorProto_Label_LABEL_REPEATED; + static inline const ::google::protobuf::EnumDescriptor* + Label_descriptor() { + return FieldDescriptorProto_Label_descriptor(); + } + static inline bool Label_IsValid(int value) { + return FieldDescriptorProto_Label_IsValid(value); + } + static const Label Label_MIN = + FieldDescriptorProto_Label_Label_MIN; + static const Label Label_MAX = + FieldDescriptorProto_Label_Label_MAX; + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional int32 number = 3; + inline bool has_number() const; + inline void clear_number(); + inline ::google::protobuf::int32 number() const; + inline void set_number(::google::protobuf::int32 value); + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + inline bool has_label() const; + inline void clear_label(); + inline ::google::protobuf::FieldDescriptorProto_Label label() const; + inline void set_label(::google::protobuf::FieldDescriptorProto_Label value); + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + inline bool has_type() const; + inline void clear_type(); + inline ::google::protobuf::FieldDescriptorProto_Type type() const; + inline void set_type(::google::protobuf::FieldDescriptorProto_Type value); + + // optional string type_name = 6; + inline bool has_type_name() const; + inline void clear_type_name(); + inline const ::std::string& type_name() const; + inline void set_type_name(const ::std::string& value); + inline void set_type_name(const char* value); + inline ::std::string* mutable_type_name(); + + // optional string extendee = 2; + inline bool has_extendee() const; + inline void clear_extendee(); + inline const ::std::string& extendee() const; + inline void set_extendee(const ::std::string& value); + inline void set_extendee(const char* value); + inline ::std::string* mutable_extendee(); + + // optional string default_value = 7; + inline bool has_default_value() const; + inline void clear_default_value(); + inline const ::std::string& default_value() const; + inline void set_default_value(const ::std::string& value); + inline void set_default_value(const char* value); + inline ::std::string* mutable_default_value(); + + // optional .google.protobuf.FieldOptions options = 8; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::FieldOptions& options() const; + inline ::google::protobuf::FieldOptions* mutable_options(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::int32 number_; + int label_; + int type_; + ::std::string* type_name_; + static const ::std::string _default_type_name_; + ::std::string* extendee_; + static const ::std::string _default_extendee_; + ::std::string* default_value_; + static const ::std::string _default_default_value_; + ::google::protobuf::FieldOptions* options_; + + static const FieldDescriptorProto default_instance_; + static const int _offsets_[8]; + + ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumDescriptorProto : public ::google::protobuf::Message { + public: + EnumDescriptorProto(); + virtual ~EnumDescriptorProto(); + + EnumDescriptorProto(const EnumDescriptorProto& from); + + inline EnumDescriptorProto& operator=(const EnumDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumDescriptorProto& from); + void MergeFrom(const EnumDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + inline int value_size() const; + inline void clear_value(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& value() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* mutable_value(); + inline const ::google::protobuf::EnumValueDescriptorProto& value(int index) const; + inline ::google::protobuf::EnumValueDescriptorProto* mutable_value(int index); + inline ::google::protobuf::EnumValueDescriptorProto* add_value(); + + // optional .google.protobuf.EnumOptions options = 3; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::EnumOptions& options() const; + inline ::google::protobuf::EnumOptions* mutable_options(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto > value_; + ::google::protobuf::EnumOptions* options_; + + static const EnumDescriptorProto default_instance_; + static const int _offsets_[3]; + + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumValueDescriptorProto : public ::google::protobuf::Message { + public: + EnumValueDescriptorProto(); + virtual ~EnumValueDescriptorProto(); + + EnumValueDescriptorProto(const EnumValueDescriptorProto& from); + + inline EnumValueDescriptorProto& operator=(const EnumValueDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumValueDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumValueDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumValueDescriptorProto& from); + void MergeFrom(const EnumValueDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional int32 number = 2; + inline bool has_number() const; + inline void clear_number(); + inline ::google::protobuf::int32 number() const; + inline void set_number(::google::protobuf::int32 value); + + // optional .google.protobuf.EnumValueOptions options = 3; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::EnumValueOptions& options() const; + inline ::google::protobuf::EnumValueOptions* mutable_options(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::int32 number_; + ::google::protobuf::EnumValueOptions* options_; + + static const EnumValueDescriptorProto default_instance_; + static const int _offsets_[3]; + + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT ServiceDescriptorProto : public ::google::protobuf::Message { + public: + ServiceDescriptorProto(); + virtual ~ServiceDescriptorProto(); + + ServiceDescriptorProto(const ServiceDescriptorProto& from); + + inline ServiceDescriptorProto& operator=(const ServiceDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const ServiceDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + ServiceDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const ServiceDescriptorProto& from); + void MergeFrom(const ServiceDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // repeated .google.protobuf.MethodDescriptorProto method = 2; + inline int method_size() const; + inline void clear_method(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& method() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* mutable_method(); + inline const ::google::protobuf::MethodDescriptorProto& method(int index) const; + inline ::google::protobuf::MethodDescriptorProto* mutable_method(int index); + inline ::google::protobuf::MethodDescriptorProto* add_method(); + + // optional .google.protobuf.ServiceOptions options = 3; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::ServiceOptions& options() const; + inline ::google::protobuf::ServiceOptions* mutable_options(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto > method_; + ::google::protobuf::ServiceOptions* options_; + + static const ServiceDescriptorProto default_instance_; + static const int _offsets_[3]; + + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT MethodDescriptorProto : public ::google::protobuf::Message { + public: + MethodDescriptorProto(); + virtual ~MethodDescriptorProto(); + + MethodDescriptorProto(const MethodDescriptorProto& from); + + inline MethodDescriptorProto& operator=(const MethodDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const MethodDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + MethodDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const MethodDescriptorProto& from); + void MergeFrom(const MethodDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional string input_type = 2; + inline bool has_input_type() const; + inline void clear_input_type(); + inline const ::std::string& input_type() const; + inline void set_input_type(const ::std::string& value); + inline void set_input_type(const char* value); + inline ::std::string* mutable_input_type(); + + // optional string output_type = 3; + inline bool has_output_type() const; + inline void clear_output_type(); + inline const ::std::string& output_type() const; + inline void set_output_type(const ::std::string& value); + inline void set_output_type(const char* value); + inline ::std::string* mutable_output_type(); + + // optional .google.protobuf.MethodOptions options = 4; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::MethodOptions& options() const; + inline ::google::protobuf::MethodOptions* mutable_options(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::std::string* input_type_; + static const ::std::string _default_input_type_; + ::std::string* output_type_; + static const ::std::string _default_output_type_; + ::google::protobuf::MethodOptions* options_; + + static const MethodDescriptorProto default_instance_; + static const int _offsets_[4]; + + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { + public: + FileOptions(); + virtual ~FileOptions(); + + FileOptions(const FileOptions& from); + + inline FileOptions& operator=(const FileOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const FileOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FileOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FileOptions& from); + void MergeFrom(const FileOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + typedef FileOptions_OptimizeMode OptimizeMode; + static const OptimizeMode SPEED = FileOptions_OptimizeMode_SPEED; + static const OptimizeMode CODE_SIZE = FileOptions_OptimizeMode_CODE_SIZE; + static inline const ::google::protobuf::EnumDescriptor* + OptimizeMode_descriptor() { + return FileOptions_OptimizeMode_descriptor(); + } + static inline bool OptimizeMode_IsValid(int value) { + return FileOptions_OptimizeMode_IsValid(value); + } + static const OptimizeMode OptimizeMode_MIN = + FileOptions_OptimizeMode_OptimizeMode_MIN; + static const OptimizeMode OptimizeMode_MAX = + FileOptions_OptimizeMode_OptimizeMode_MAX; + + // accessors ------------------------------------------------------- + + // optional string java_package = 1; + inline bool has_java_package() const; + inline void clear_java_package(); + inline const ::std::string& java_package() const; + inline void set_java_package(const ::std::string& value); + inline void set_java_package(const char* value); + inline ::std::string* mutable_java_package(); + + // optional string java_outer_classname = 8; + inline bool has_java_outer_classname() const; + inline void clear_java_outer_classname(); + inline const ::std::string& java_outer_classname() const; + inline void set_java_outer_classname(const ::std::string& value); + inline void set_java_outer_classname(const char* value); + inline ::std::string* mutable_java_outer_classname(); + + // optional bool java_multiple_files = 10 [default = false]; + inline bool has_java_multiple_files() const; + inline void clear_java_multiple_files(); + inline bool java_multiple_files() const; + inline void set_java_multiple_files(bool value); + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + inline bool has_optimize_for() const; + inline void clear_optimize_for(); + inline ::google::protobuf::FileOptions_OptimizeMode optimize_for() const; + inline void set_optimize_for(::google::protobuf::FileOptions_OptimizeMode value); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + ::std::string* java_package_; + static const ::std::string _default_java_package_; + ::std::string* java_outer_classname_; + static const ::std::string _default_java_outer_classname_; + bool java_multiple_files_; + int optimize_for_; + + static const FileOptions default_instance_; + static const int _offsets_[4]; + + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT MessageOptions : public ::google::protobuf::Message { + public: + MessageOptions(); + virtual ~MessageOptions(); + + MessageOptions(const MessageOptions& from); + + inline MessageOptions& operator=(const MessageOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const MessageOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + MessageOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const MessageOptions& from); + void MergeFrom(const MessageOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional bool message_set_wire_format = 1 [default = false]; + inline bool has_message_set_wire_format() const; + inline void clear_message_set_wire_format(); + inline bool message_set_wire_format() const; + inline void set_message_set_wire_format(bool value); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + bool message_set_wire_format_; + + static const MessageOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { + public: + FieldOptions(); + virtual ~FieldOptions(); + + FieldOptions(const FieldOptions& from); + + inline FieldOptions& operator=(const FieldOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const FieldOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FieldOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FieldOptions& from); + void MergeFrom(const FieldOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + typedef FieldOptions_CType CType; + static const CType CORD = FieldOptions_CType_CORD; + static const CType STRING_PIECE = FieldOptions_CType_STRING_PIECE; + static inline const ::google::protobuf::EnumDescriptor* + CType_descriptor() { + return FieldOptions_CType_descriptor(); + } + static inline bool CType_IsValid(int value) { + return FieldOptions_CType_IsValid(value); + } + static const CType CType_MIN = + FieldOptions_CType_CType_MIN; + static const CType CType_MAX = + FieldOptions_CType_CType_MAX; + + // accessors ------------------------------------------------------- + + // optional .google.protobuf.FieldOptions.CType ctype = 1; + inline bool has_ctype() const; + inline void clear_ctype(); + inline ::google::protobuf::FieldOptions_CType ctype() const; + inline void set_ctype(::google::protobuf::FieldOptions_CType value); + + // optional string experimental_map_key = 9; + inline bool has_experimental_map_key() const; + inline void clear_experimental_map_key(); + inline const ::std::string& experimental_map_key() const; + inline void set_experimental_map_key(const ::std::string& value); + inline void set_experimental_map_key(const char* value); + inline ::std::string* mutable_experimental_map_key(); + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + int ctype_; + ::std::string* experimental_map_key_; + static const ::std::string _default_experimental_map_key_; + + static const FieldOptions default_instance_; + static const int _offsets_[2]; + + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumOptions : public ::google::protobuf::Message { + public: + EnumOptions(); + virtual ~EnumOptions(); + + EnumOptions(const EnumOptions& from); + + inline EnumOptions& operator=(const EnumOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumOptions& from); + void MergeFrom(const EnumOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + + static const EnumOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumValueOptions : public ::google::protobuf::Message { + public: + EnumValueOptions(); + virtual ~EnumValueOptions(); + + EnumValueOptions(const EnumValueOptions& from); + + inline EnumValueOptions& operator=(const EnumValueOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumValueOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumValueOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumValueOptions& from); + void MergeFrom(const EnumValueOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + + static const EnumValueOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT ServiceOptions : public ::google::protobuf::Message { + public: + ServiceOptions(); + virtual ~ServiceOptions(); + + ServiceOptions(const ServiceOptions& from); + + inline ServiceOptions& operator=(const ServiceOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const ServiceOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + ServiceOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const ServiceOptions& from); + void MergeFrom(const ServiceOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + + static const ServiceOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT MethodOptions : public ::google::protobuf::Message { + public: + MethodOptions(); + virtual ~MethodOptions(); + + MethodOptions(const MethodOptions& from); + + inline MethodOptions& operator=(const MethodOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const MethodOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _reflection_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _reflection_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + MethodOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const MethodOptions& from); + void MergeFrom(const MethodOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Message::Reflection* GetReflection() const; + ::google::protobuf::Message::Reflection* GetReflection(); + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::internal::GeneratedMessageReflection _reflection_; + mutable int _cached_size_; + + + static const MethodOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// =================================================================== + + +// =================================================================== + + +// =================================================================== + +// FileDescriptorProto + +// optional string name = 1; +inline bool FileDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void FileDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& FileDescriptorProto::name() const { + return *name_; +} +inline void FileDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void FileDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* FileDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional string package = 2; +inline bool FileDescriptorProto::has_package() const { + return _has_bit(1); +} +inline void FileDescriptorProto::clear_package() { + if (package_ != &_default_package_) { + package_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& FileDescriptorProto::package() const { + return *package_; +} +inline void FileDescriptorProto::set_package(const ::std::string& value) { + _set_bit(1); + if (package_ == &_default_package_) { + package_ = new ::std::string; + } + package_->assign(value); +} +inline void FileDescriptorProto::set_package(const char* value) { + _set_bit(1); + if (package_ == &_default_package_) { + package_ = new ::std::string; + } + package_->assign(value); +} +inline ::std::string* FileDescriptorProto::mutable_package() { + _set_bit(1); + if (package_ == &_default_package_) { + package_ = new ::std::string; + } + return package_; +} + +// repeated string dependency = 3; +inline int FileDescriptorProto::dependency_size() const { + return dependency_.size(); +} +inline void FileDescriptorProto::clear_dependency() { + dependency_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +FileDescriptorProto::dependency() const { + return dependency_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +FileDescriptorProto::mutable_dependency() { + return &dependency_; +} +inline const ::std::string& FileDescriptorProto::dependency(int index) const { + return dependency_.Get(index); +} +inline ::std::string* FileDescriptorProto::mutable_dependency(int index) { + return dependency_.Mutable(index); +} +inline void FileDescriptorProto::set_dependency(int index, const ::std::string& value) { + dependency_.Mutable(index)->assign(value); +} +inline void FileDescriptorProto::set_dependency(int index, const char* value) { + dependency_.Mutable(index)->assign(value); +} +inline ::std::string* FileDescriptorProto::add_dependency() { + return dependency_.Add(); +} +inline void FileDescriptorProto::add_dependency(const ::std::string& value) { + dependency_.Add()->assign(value); +} +inline void FileDescriptorProto::add_dependency(const char* value) { + dependency_.Add()->assign(value); +} + +// repeated .google.protobuf.DescriptorProto message_type = 4; +inline int FileDescriptorProto::message_type_size() const { + return message_type_.size(); +} +inline void FileDescriptorProto::clear_message_type() { + message_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +FileDescriptorProto::message_type() const { + return message_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* +FileDescriptorProto::mutable_message_type() { + return &message_type_; +} +inline const ::google::protobuf::DescriptorProto& FileDescriptorProto::message_type(int index) const { + return message_type_.Get(index); +} +inline ::google::protobuf::DescriptorProto* FileDescriptorProto::mutable_message_type(int index) { + return message_type_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto* FileDescriptorProto::add_message_type() { + return message_type_.Add(); +} + +// repeated .google.protobuf.EnumDescriptorProto enum_type = 5; +inline int FileDescriptorProto::enum_type_size() const { + return enum_type_.size(); +} +inline void FileDescriptorProto::clear_enum_type() { + enum_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +FileDescriptorProto::enum_type() const { + return enum_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* +FileDescriptorProto::mutable_enum_type() { + return &enum_type_; +} +inline const ::google::protobuf::EnumDescriptorProto& FileDescriptorProto::enum_type(int index) const { + return enum_type_.Get(index); +} +inline ::google::protobuf::EnumDescriptorProto* FileDescriptorProto::mutable_enum_type(int index) { + return enum_type_.Mutable(index); +} +inline ::google::protobuf::EnumDescriptorProto* FileDescriptorProto::add_enum_type() { + return enum_type_.Add(); +} + +// repeated .google.protobuf.ServiceDescriptorProto service = 6; +inline int FileDescriptorProto::service_size() const { + return service_.size(); +} +inline void FileDescriptorProto::clear_service() { + service_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& +FileDescriptorProto::service() const { + return service_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* +FileDescriptorProto::mutable_service() { + return &service_; +} +inline const ::google::protobuf::ServiceDescriptorProto& FileDescriptorProto::service(int index) const { + return service_.Get(index); +} +inline ::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::mutable_service(int index) { + return service_.Mutable(index); +} +inline ::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::add_service() { + return service_.Add(); +} + +// repeated .google.protobuf.FieldDescriptorProto extension = 7; +inline int FileDescriptorProto::extension_size() const { + return extension_.size(); +} +inline void FileDescriptorProto::clear_extension() { + extension_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +FileDescriptorProto::extension() const { + return extension_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +FileDescriptorProto::mutable_extension() { + return &extension_; +} +inline const ::google::protobuf::FieldDescriptorProto& FileDescriptorProto::extension(int index) const { + return extension_.Get(index); +} +inline ::google::protobuf::FieldDescriptorProto* FileDescriptorProto::mutable_extension(int index) { + return extension_.Mutable(index); +} +inline ::google::protobuf::FieldDescriptorProto* FileDescriptorProto::add_extension() { + return extension_.Add(); +} + +// optional .google.protobuf.FileOptions options = 8; +inline bool FileDescriptorProto::has_options() const { + return _has_bit(7); +} +inline void FileDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); + _clear_bit(7); +} +inline const ::google::protobuf::FileOptions& FileDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::FileOptions* FileDescriptorProto::mutable_options() { + _set_bit(7); + if (options_ == NULL) options_ = new ::google::protobuf::FileOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// DescriptorProto_ExtensionRange + +// optional int32 start = 1; +inline bool DescriptorProto_ExtensionRange::has_start() const { + return _has_bit(0); +} +inline void DescriptorProto_ExtensionRange::clear_start() { + start_ = 0; + _clear_bit(0); +} +inline ::google::protobuf::int32 DescriptorProto_ExtensionRange::start() const { + return start_; +} +inline void DescriptorProto_ExtensionRange::set_start(::google::protobuf::int32 value) { + _set_bit(0); + start_ = value; +} + +// optional int32 end = 2; +inline bool DescriptorProto_ExtensionRange::has_end() const { + return _has_bit(1); +} +inline void DescriptorProto_ExtensionRange::clear_end() { + end_ = 0; + _clear_bit(1); +} +inline ::google::protobuf::int32 DescriptorProto_ExtensionRange::end() const { + return end_; +} +inline void DescriptorProto_ExtensionRange::set_end(::google::protobuf::int32 value) { + _set_bit(1); + end_ = value; +} + +// ------------------------------------------------------------------- + +// DescriptorProto + +// optional string name = 1; +inline bool DescriptorProto::has_name() const { + return _has_bit(0); +} +inline void DescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& DescriptorProto::name() const { + return *name_; +} +inline void DescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void DescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* DescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// repeated .google.protobuf.FieldDescriptorProto field = 2; +inline int DescriptorProto::field_size() const { + return field_.size(); +} +inline void DescriptorProto::clear_field() { + field_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::field() const { + return field_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +DescriptorProto::mutable_field() { + return &field_; +} +inline const ::google::protobuf::FieldDescriptorProto& DescriptorProto::field(int index) const { + return field_.Get(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_field(int index) { + return field_.Mutable(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_field() { + return field_.Add(); +} + +// repeated .google.protobuf.FieldDescriptorProto extension = 6; +inline int DescriptorProto::extension_size() const { + return extension_.size(); +} +inline void DescriptorProto::clear_extension() { + extension_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::extension() const { + return extension_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +DescriptorProto::mutable_extension() { + return &extension_; +} +inline const ::google::protobuf::FieldDescriptorProto& DescriptorProto::extension(int index) const { + return extension_.Get(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_extension(int index) { + return extension_.Mutable(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_extension() { + return extension_.Add(); +} + +// repeated .google.protobuf.DescriptorProto nested_type = 3; +inline int DescriptorProto::nested_type_size() const { + return nested_type_.size(); +} +inline void DescriptorProto::clear_nested_type() { + nested_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +DescriptorProto::nested_type() const { + return nested_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* +DescriptorProto::mutable_nested_type() { + return &nested_type_; +} +inline const ::google::protobuf::DescriptorProto& DescriptorProto::nested_type(int index) const { + return nested_type_.Get(index); +} +inline ::google::protobuf::DescriptorProto* DescriptorProto::mutable_nested_type(int index) { + return nested_type_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto* DescriptorProto::add_nested_type() { + return nested_type_.Add(); +} + +// repeated .google.protobuf.EnumDescriptorProto enum_type = 4; +inline int DescriptorProto::enum_type_size() const { + return enum_type_.size(); +} +inline void DescriptorProto::clear_enum_type() { + enum_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +DescriptorProto::enum_type() const { + return enum_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* +DescriptorProto::mutable_enum_type() { + return &enum_type_; +} +inline const ::google::protobuf::EnumDescriptorProto& DescriptorProto::enum_type(int index) const { + return enum_type_.Get(index); +} +inline ::google::protobuf::EnumDescriptorProto* DescriptorProto::mutable_enum_type(int index) { + return enum_type_.Mutable(index); +} +inline ::google::protobuf::EnumDescriptorProto* DescriptorProto::add_enum_type() { + return enum_type_.Add(); +} + +// repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; +inline int DescriptorProto::extension_range_size() const { + return extension_range_.size(); +} +inline void DescriptorProto::clear_extension_range() { + extension_range_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& +DescriptorProto::extension_range() const { + return extension_range_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* +DescriptorProto::mutable_extension_range() { + return &extension_range_; +} +inline const ::google::protobuf::DescriptorProto_ExtensionRange& DescriptorProto::extension_range(int index) const { + return extension_range_.Get(index); +} +inline ::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::mutable_extension_range(int index) { + return extension_range_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::add_extension_range() { + return extension_range_.Add(); +} + +// optional .google.protobuf.MessageOptions options = 7; +inline bool DescriptorProto::has_options() const { + return _has_bit(6); +} +inline void DescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); + _clear_bit(6); +} +inline const ::google::protobuf::MessageOptions& DescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::MessageOptions* DescriptorProto::mutable_options() { + _set_bit(6); + if (options_ == NULL) options_ = new ::google::protobuf::MessageOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// FieldDescriptorProto + +// optional string name = 1; +inline bool FieldDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void FieldDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& FieldDescriptorProto::name() const { + return *name_; +} +inline void FieldDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void FieldDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional int32 number = 3; +inline bool FieldDescriptorProto::has_number() const { + return _has_bit(1); +} +inline void FieldDescriptorProto::clear_number() { + number_ = 0; + _clear_bit(1); +} +inline ::google::protobuf::int32 FieldDescriptorProto::number() const { + return number_; +} +inline void FieldDescriptorProto::set_number(::google::protobuf::int32 value) { + _set_bit(1); + number_ = value; +} + +// optional .google.protobuf.FieldDescriptorProto.Label label = 4; +inline bool FieldDescriptorProto::has_label() const { + return _has_bit(2); +} +inline void FieldDescriptorProto::clear_label() { + label_ = 1; + _clear_bit(2); +} +inline ::google::protobuf::FieldDescriptorProto_Label FieldDescriptorProto::label() const { + return static_cast< ::google::protobuf::FieldDescriptorProto_Label >(label_); +} +inline void FieldDescriptorProto::set_label(::google::protobuf::FieldDescriptorProto_Label value) { + GOOGLE_DCHECK(::google::protobuf::FieldDescriptorProto_Label_IsValid(value)); + _set_bit(2); + label_ = value; +} + +// optional .google.protobuf.FieldDescriptorProto.Type type = 5; +inline bool FieldDescriptorProto::has_type() const { + return _has_bit(3); +} +inline void FieldDescriptorProto::clear_type() { + type_ = 1; + _clear_bit(3); +} +inline ::google::protobuf::FieldDescriptorProto_Type FieldDescriptorProto::type() const { + return static_cast< ::google::protobuf::FieldDescriptorProto_Type >(type_); +} +inline void FieldDescriptorProto::set_type(::google::protobuf::FieldDescriptorProto_Type value) { + GOOGLE_DCHECK(::google::protobuf::FieldDescriptorProto_Type_IsValid(value)); + _set_bit(3); + type_ = value; +} + +// optional string type_name = 6; +inline bool FieldDescriptorProto::has_type_name() const { + return _has_bit(4); +} +inline void FieldDescriptorProto::clear_type_name() { + if (type_name_ != &_default_type_name_) { + type_name_->clear(); + } + _clear_bit(4); +} +inline const ::std::string& FieldDescriptorProto::type_name() const { + return *type_name_; +} +inline void FieldDescriptorProto::set_type_name(const ::std::string& value) { + _set_bit(4); + if (type_name_ == &_default_type_name_) { + type_name_ = new ::std::string; + } + type_name_->assign(value); +} +inline void FieldDescriptorProto::set_type_name(const char* value) { + _set_bit(4); + if (type_name_ == &_default_type_name_) { + type_name_ = new ::std::string; + } + type_name_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_type_name() { + _set_bit(4); + if (type_name_ == &_default_type_name_) { + type_name_ = new ::std::string; + } + return type_name_; +} + +// optional string extendee = 2; +inline bool FieldDescriptorProto::has_extendee() const { + return _has_bit(5); +} +inline void FieldDescriptorProto::clear_extendee() { + if (extendee_ != &_default_extendee_) { + extendee_->clear(); + } + _clear_bit(5); +} +inline const ::std::string& FieldDescriptorProto::extendee() const { + return *extendee_; +} +inline void FieldDescriptorProto::set_extendee(const ::std::string& value) { + _set_bit(5); + if (extendee_ == &_default_extendee_) { + extendee_ = new ::std::string; + } + extendee_->assign(value); +} +inline void FieldDescriptorProto::set_extendee(const char* value) { + _set_bit(5); + if (extendee_ == &_default_extendee_) { + extendee_ = new ::std::string; + } + extendee_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_extendee() { + _set_bit(5); + if (extendee_ == &_default_extendee_) { + extendee_ = new ::std::string; + } + return extendee_; +} + +// optional string default_value = 7; +inline bool FieldDescriptorProto::has_default_value() const { + return _has_bit(6); +} +inline void FieldDescriptorProto::clear_default_value() { + if (default_value_ != &_default_default_value_) { + default_value_->clear(); + } + _clear_bit(6); +} +inline const ::std::string& FieldDescriptorProto::default_value() const { + return *default_value_; +} +inline void FieldDescriptorProto::set_default_value(const ::std::string& value) { + _set_bit(6); + if (default_value_ == &_default_default_value_) { + default_value_ = new ::std::string; + } + default_value_->assign(value); +} +inline void FieldDescriptorProto::set_default_value(const char* value) { + _set_bit(6); + if (default_value_ == &_default_default_value_) { + default_value_ = new ::std::string; + } + default_value_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_default_value() { + _set_bit(6); + if (default_value_ == &_default_default_value_) { + default_value_ = new ::std::string; + } + return default_value_; +} + +// optional .google.protobuf.FieldOptions options = 8; +inline bool FieldDescriptorProto::has_options() const { + return _has_bit(7); +} +inline void FieldDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); + _clear_bit(7); +} +inline const ::google::protobuf::FieldOptions& FieldDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::FieldOptions* FieldDescriptorProto::mutable_options() { + _set_bit(7); + if (options_ == NULL) options_ = new ::google::protobuf::FieldOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// EnumDescriptorProto + +// optional string name = 1; +inline bool EnumDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void EnumDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& EnumDescriptorProto::name() const { + return *name_; +} +inline void EnumDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void EnumDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* EnumDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// repeated .google.protobuf.EnumValueDescriptorProto value = 2; +inline int EnumDescriptorProto::value_size() const { + return value_.size(); +} +inline void EnumDescriptorProto::clear_value() { + value_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& +EnumDescriptorProto::value() const { + return value_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* +EnumDescriptorProto::mutable_value() { + return &value_; +} +inline const ::google::protobuf::EnumValueDescriptorProto& EnumDescriptorProto::value(int index) const { + return value_.Get(index); +} +inline ::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::mutable_value(int index) { + return value_.Mutable(index); +} +inline ::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::add_value() { + return value_.Add(); +} + +// optional .google.protobuf.EnumOptions options = 3; +inline bool EnumDescriptorProto::has_options() const { + return _has_bit(2); +} +inline void EnumDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); + _clear_bit(2); +} +inline const ::google::protobuf::EnumOptions& EnumDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::EnumOptions* EnumDescriptorProto::mutable_options() { + _set_bit(2); + if (options_ == NULL) options_ = new ::google::protobuf::EnumOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// EnumValueDescriptorProto + +// optional string name = 1; +inline bool EnumValueDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void EnumValueDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& EnumValueDescriptorProto::name() const { + return *name_; +} +inline void EnumValueDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void EnumValueDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* EnumValueDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional int32 number = 2; +inline bool EnumValueDescriptorProto::has_number() const { + return _has_bit(1); +} +inline void EnumValueDescriptorProto::clear_number() { + number_ = 0; + _clear_bit(1); +} +inline ::google::protobuf::int32 EnumValueDescriptorProto::number() const { + return number_; +} +inline void EnumValueDescriptorProto::set_number(::google::protobuf::int32 value) { + _set_bit(1); + number_ = value; +} + +// optional .google.protobuf.EnumValueOptions options = 3; +inline bool EnumValueDescriptorProto::has_options() const { + return _has_bit(2); +} +inline void EnumValueDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); + _clear_bit(2); +} +inline const ::google::protobuf::EnumValueOptions& EnumValueDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::mutable_options() { + _set_bit(2); + if (options_ == NULL) options_ = new ::google::protobuf::EnumValueOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// ServiceDescriptorProto + +// optional string name = 1; +inline bool ServiceDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void ServiceDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& ServiceDescriptorProto::name() const { + return *name_; +} +inline void ServiceDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void ServiceDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* ServiceDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// repeated .google.protobuf.MethodDescriptorProto method = 2; +inline int ServiceDescriptorProto::method_size() const { + return method_.size(); +} +inline void ServiceDescriptorProto::clear_method() { + method_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& +ServiceDescriptorProto::method() const { + return method_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* +ServiceDescriptorProto::mutable_method() { + return &method_; +} +inline const ::google::protobuf::MethodDescriptorProto& ServiceDescriptorProto::method(int index) const { + return method_.Get(index); +} +inline ::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::mutable_method(int index) { + return method_.Mutable(index); +} +inline ::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::add_method() { + return method_.Add(); +} + +// optional .google.protobuf.ServiceOptions options = 3; +inline bool ServiceDescriptorProto::has_options() const { + return _has_bit(2); +} +inline void ServiceDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); + _clear_bit(2); +} +inline const ::google::protobuf::ServiceOptions& ServiceDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::ServiceOptions* ServiceDescriptorProto::mutable_options() { + _set_bit(2); + if (options_ == NULL) options_ = new ::google::protobuf::ServiceOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// MethodDescriptorProto + +// optional string name = 1; +inline bool MethodDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void MethodDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& MethodDescriptorProto::name() const { + return *name_; +} +inline void MethodDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void MethodDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* MethodDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional string input_type = 2; +inline bool MethodDescriptorProto::has_input_type() const { + return _has_bit(1); +} +inline void MethodDescriptorProto::clear_input_type() { + if (input_type_ != &_default_input_type_) { + input_type_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& MethodDescriptorProto::input_type() const { + return *input_type_; +} +inline void MethodDescriptorProto::set_input_type(const ::std::string& value) { + _set_bit(1); + if (input_type_ == &_default_input_type_) { + input_type_ = new ::std::string; + } + input_type_->assign(value); +} +inline void MethodDescriptorProto::set_input_type(const char* value) { + _set_bit(1); + if (input_type_ == &_default_input_type_) { + input_type_ = new ::std::string; + } + input_type_->assign(value); +} +inline ::std::string* MethodDescriptorProto::mutable_input_type() { + _set_bit(1); + if (input_type_ == &_default_input_type_) { + input_type_ = new ::std::string; + } + return input_type_; +} + +// optional string output_type = 3; +inline bool MethodDescriptorProto::has_output_type() const { + return _has_bit(2); +} +inline void MethodDescriptorProto::clear_output_type() { + if (output_type_ != &_default_output_type_) { + output_type_->clear(); + } + _clear_bit(2); +} +inline const ::std::string& MethodDescriptorProto::output_type() const { + return *output_type_; +} +inline void MethodDescriptorProto::set_output_type(const ::std::string& value) { + _set_bit(2); + if (output_type_ == &_default_output_type_) { + output_type_ = new ::std::string; + } + output_type_->assign(value); +} +inline void MethodDescriptorProto::set_output_type(const char* value) { + _set_bit(2); + if (output_type_ == &_default_output_type_) { + output_type_ = new ::std::string; + } + output_type_->assign(value); +} +inline ::std::string* MethodDescriptorProto::mutable_output_type() { + _set_bit(2); + if (output_type_ == &_default_output_type_) { + output_type_ = new ::std::string; + } + return output_type_; +} + +// optional .google.protobuf.MethodOptions options = 4; +inline bool MethodDescriptorProto::has_options() const { + return _has_bit(3); +} +inline void MethodDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); + _clear_bit(3); +} +inline const ::google::protobuf::MethodOptions& MethodDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::MethodOptions* MethodDescriptorProto::mutable_options() { + _set_bit(3); + if (options_ == NULL) options_ = new ::google::protobuf::MethodOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// FileOptions + +// optional string java_package = 1; +inline bool FileOptions::has_java_package() const { + return _has_bit(0); +} +inline void FileOptions::clear_java_package() { + if (java_package_ != &_default_java_package_) { + java_package_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& FileOptions::java_package() const { + return *java_package_; +} +inline void FileOptions::set_java_package(const ::std::string& value) { + _set_bit(0); + if (java_package_ == &_default_java_package_) { + java_package_ = new ::std::string; + } + java_package_->assign(value); +} +inline void FileOptions::set_java_package(const char* value) { + _set_bit(0); + if (java_package_ == &_default_java_package_) { + java_package_ = new ::std::string; + } + java_package_->assign(value); +} +inline ::std::string* FileOptions::mutable_java_package() { + _set_bit(0); + if (java_package_ == &_default_java_package_) { + java_package_ = new ::std::string; + } + return java_package_; +} + +// optional string java_outer_classname = 8; +inline bool FileOptions::has_java_outer_classname() const { + return _has_bit(1); +} +inline void FileOptions::clear_java_outer_classname() { + if (java_outer_classname_ != &_default_java_outer_classname_) { + java_outer_classname_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& FileOptions::java_outer_classname() const { + return *java_outer_classname_; +} +inline void FileOptions::set_java_outer_classname(const ::std::string& value) { + _set_bit(1); + if (java_outer_classname_ == &_default_java_outer_classname_) { + java_outer_classname_ = new ::std::string; + } + java_outer_classname_->assign(value); +} +inline void FileOptions::set_java_outer_classname(const char* value) { + _set_bit(1); + if (java_outer_classname_ == &_default_java_outer_classname_) { + java_outer_classname_ = new ::std::string; + } + java_outer_classname_->assign(value); +} +inline ::std::string* FileOptions::mutable_java_outer_classname() { + _set_bit(1); + if (java_outer_classname_ == &_default_java_outer_classname_) { + java_outer_classname_ = new ::std::string; + } + return java_outer_classname_; +} + +// optional bool java_multiple_files = 10 [default = false]; +inline bool FileOptions::has_java_multiple_files() const { + return _has_bit(2); +} +inline void FileOptions::clear_java_multiple_files() { + java_multiple_files_ = false; + _clear_bit(2); +} +inline bool FileOptions::java_multiple_files() const { + return java_multiple_files_; +} +inline void FileOptions::set_java_multiple_files(bool value) { + _set_bit(2); + java_multiple_files_ = value; +} + +// optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; +inline bool FileOptions::has_optimize_for() const { + return _has_bit(3); +} +inline void FileOptions::clear_optimize_for() { + optimize_for_ = 2; + _clear_bit(3); +} +inline ::google::protobuf::FileOptions_OptimizeMode FileOptions::optimize_for() const { + return static_cast< ::google::protobuf::FileOptions_OptimizeMode >(optimize_for_); +} +inline void FileOptions::set_optimize_for(::google::protobuf::FileOptions_OptimizeMode value) { + GOOGLE_DCHECK(::google::protobuf::FileOptions_OptimizeMode_IsValid(value)); + _set_bit(3); + optimize_for_ = value; +} + +// ------------------------------------------------------------------- + +// MessageOptions + +// optional bool message_set_wire_format = 1 [default = false]; +inline bool MessageOptions::has_message_set_wire_format() const { + return _has_bit(0); +} +inline void MessageOptions::clear_message_set_wire_format() { + message_set_wire_format_ = false; + _clear_bit(0); +} +inline bool MessageOptions::message_set_wire_format() const { + return message_set_wire_format_; +} +inline void MessageOptions::set_message_set_wire_format(bool value) { + _set_bit(0); + message_set_wire_format_ = value; +} + +// ------------------------------------------------------------------- + +// FieldOptions + +// optional .google.protobuf.FieldOptions.CType ctype = 1; +inline bool FieldOptions::has_ctype() const { + return _has_bit(0); +} +inline void FieldOptions::clear_ctype() { + ctype_ = 1; + _clear_bit(0); +} +inline ::google::protobuf::FieldOptions_CType FieldOptions::ctype() const { + return static_cast< ::google::protobuf::FieldOptions_CType >(ctype_); +} +inline void FieldOptions::set_ctype(::google::protobuf::FieldOptions_CType value) { + GOOGLE_DCHECK(::google::protobuf::FieldOptions_CType_IsValid(value)); + _set_bit(0); + ctype_ = value; +} + +// optional string experimental_map_key = 9; +inline bool FieldOptions::has_experimental_map_key() const { + return _has_bit(1); +} +inline void FieldOptions::clear_experimental_map_key() { + if (experimental_map_key_ != &_default_experimental_map_key_) { + experimental_map_key_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& FieldOptions::experimental_map_key() const { + return *experimental_map_key_; +} +inline void FieldOptions::set_experimental_map_key(const ::std::string& value) { + _set_bit(1); + if (experimental_map_key_ == &_default_experimental_map_key_) { + experimental_map_key_ = new ::std::string; + } + experimental_map_key_->assign(value); +} +inline void FieldOptions::set_experimental_map_key(const char* value) { + _set_bit(1); + if (experimental_map_key_ == &_default_experimental_map_key_) { + experimental_map_key_ = new ::std::string; + } + experimental_map_key_->assign(value); +} +inline ::std::string* FieldOptions::mutable_experimental_map_key() { + _set_bit(1); + if (experimental_map_key_ == &_default_experimental_map_key_) { + experimental_map_key_ = new ::std::string; + } + return experimental_map_key_; +} + +// ------------------------------------------------------------------- + +// EnumOptions + +// ------------------------------------------------------------------- + +// EnumValueOptions + +// ------------------------------------------------------------------- + +// ServiceOptions + +// ------------------------------------------------------------------- + +// MethodOptions + + +} // namespace protobuf +} // namespace google +#endif // PROTOBUF_google_2fprotobuf_2fdescriptor_2eproto__INCLUDED diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto new file mode 100644 index 00000000..13d94780 --- /dev/null +++ b/src/google/protobuf/descriptor.proto @@ -0,0 +1,286 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + + + +package google.protobuf; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + } + repeated ExtensionRange extension_range = 5; + + optional MessageOptions options = 7; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; // Not ZigZag encoded. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. + TYPE_UINT64 = 4; + TYPE_INT32 = 5; // Not ZigZag encoded. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; // Tag-delimited aggregate. + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + }; + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + // TODO(sanjay): Should we add LABEL_MAP? + }; + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be either TYPE_ENUM or TYPE_MESSAGE. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + optional string default_value = 7; + + optional FieldOptions options = 8; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; +} + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. + +// TODO(kenton): Allow extensions to options. + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + optional string java_outer_classname = 8; + + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default=false]; + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + } + optional OptimizeMode optimize_for = 9 [default=CODE_SIZE]; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default=false]; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1; + enum CType { + CORD = 1; + + STRING_PIECE = 2; + } + + // EXPERIMENTAL. DO NOT USE. + // For "map" fields, the name of the field in the enclosed type that + // is the key for this map. For example, suppose we have: + // message Item { + // required string name = 1; + // required string value = 2; + // } + // message Config { + // repeated Item items = 1 [experimental_map_key="name"]; + // } + // In this situation, the map key for Item will be set to "name". + // TODO: Fully-implement this, then remove the "experimental_" prefix. + optional string experimental_map_key = 9; +} + +message EnumOptions { +} + +message EnumValueOptions { +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. +} diff --git a/src/google/protobuf/descriptor_database.cc b/src/google/protobuf/descriptor_database.cc new file mode 100644 index 00000000..944280c0 --- /dev/null +++ b/src/google/protobuf/descriptor_database.cc @@ -0,0 +1,291 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +DescriptorDatabase::~DescriptorDatabase() {} + +// =================================================================== + +SimpleDescriptorDatabase::SimpleDescriptorDatabase() {} +SimpleDescriptorDatabase::~SimpleDescriptorDatabase() { + STLDeleteElements(&files_to_delete_); +} + +void SimpleDescriptorDatabase::Add(const FileDescriptorProto& file) { + FileDescriptorProto* new_file = new FileDescriptorProto; + new_file->CopyFrom(file); + AddAndOwn(new_file); +} + +void SimpleDescriptorDatabase::AddAndOwn(const FileDescriptorProto* file) { + files_to_delete_.push_back(file); + InsertOrUpdate(&files_by_name_, file->name(), file); + + string path = file->package(); + if (!path.empty()) path += '.'; + + for (int i = 0; i < file->message_type_size(); i++) { + AddMessage(path, file->message_type(i), file); + } + for (int i = 0; i < file->enum_type_size(); i++) { + AddEnum(path, file->enum_type(i), file); + } + for (int i = 0; i < file->extension_size(); i++) { + AddField(path, file->extension(i), file); + } + for (int i = 0; i < file->service_size(); i++) { + AddService(path, file->service(i), file); + } +} + +void SimpleDescriptorDatabase::AddMessage( + const string& path, + const DescriptorProto& message_type, + const FileDescriptorProto* file) { + string full_name = path + message_type.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + string sub_path = full_name + '.'; + for (int i = 0; i < message_type.nested_type_size(); i++) { + AddMessage(sub_path, message_type.nested_type(i), file); + } + for (int i = 0; i < message_type.enum_type_size(); i++) { + AddEnum(sub_path, message_type.enum_type(i), file); + } + for (int i = 0; i < message_type.field_size(); i++) { + AddField(sub_path, message_type.field(i), file); + } + for (int i = 0; i < message_type.extension_size(); i++) { + AddField(sub_path, message_type.extension(i), file); + } +} + +void SimpleDescriptorDatabase::AddField( + const string& path, + const FieldDescriptorProto& field, + const FileDescriptorProto* file) { + string full_name = path + field.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + if (field.has_extendee()) { + // This field is an extension. + if (!field.extendee().empty() && field.extendee()[0] == '.') { + // The extension is fully-qualified. We can use it as a lookup key in + // the files_by_symbol_ table. + InsertOrUpdate(&files_by_extension_, + make_pair(field.extendee().substr(1), field.number()), + file); + } else { + // Not fully-qualified. We can't really do anything here, unfortunately. + } + } +} + +void SimpleDescriptorDatabase::AddEnum( + const string& path, + const EnumDescriptorProto& enum_type, + const FileDescriptorProto* file) { + string full_name = path + enum_type.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + string sub_path = full_name + '.'; + for (int i = 0; i < enum_type.value_size(); i++) { + InsertOrUpdate(&files_by_symbol_, + sub_path + enum_type.value(i).name(), + file); + } +} + +void SimpleDescriptorDatabase::AddService( + const string& path, + const ServiceDescriptorProto& service, + const FileDescriptorProto* file) { + string full_name = path + service.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + string sub_path = full_name + '.'; + for (int i = 0; i < service.method_size(); i++) { + InsertOrUpdate(&files_by_symbol_, + sub_path + service.method(i).name(), + file); + } +} + +bool SimpleDescriptorDatabase::FindFileByName( + const string& filename, + FileDescriptorProto* output) { + const FileDescriptorProto* result = FindPtrOrNull(files_by_name_, filename); + if (result == NULL) { + return false; + } else { + output->CopyFrom(*result); + return true; + } +} + +bool SimpleDescriptorDatabase::FindFileContainingSymbol( + const string& symbol_name, + FileDescriptorProto* output) { + const FileDescriptorProto* result = + FindPtrOrNull(files_by_symbol_, symbol_name); + if (result == NULL) { + return false; + } else { + output->CopyFrom(*result); + return true; + } +} + +bool SimpleDescriptorDatabase::FindFileContainingExtension( + const string& containing_type, + int field_number, + FileDescriptorProto* output) { + const FileDescriptorProto* result = + FindPtrOrNull(files_by_extension_, + make_pair(containing_type, field_number)); + if (result == NULL) { + return false; + } else { + output->CopyFrom(*result); + return true; + } +} + +// =================================================================== + +DescriptorPoolDatabase::DescriptorPoolDatabase(const DescriptorPool& pool) + : pool_(pool) {} +DescriptorPoolDatabase::~DescriptorPoolDatabase() {} + +bool DescriptorPoolDatabase::FindFileByName( + const string& filename, + FileDescriptorProto* output) { + const FileDescriptor* file = pool_.FindFileByName(filename); + if (file == NULL) return false; + output->Clear(); + file->CopyTo(output); + return true; +} + +bool DescriptorPoolDatabase::FindFileContainingSymbol( + const string& symbol_name, + FileDescriptorProto* output) { + const FileDescriptor* file = pool_.FindFileContainingSymbol(symbol_name); + if (file == NULL) return false; + output->Clear(); + file->CopyTo(output); + return true; +} + +bool DescriptorPoolDatabase::FindFileContainingExtension( + const string& containing_type, + int field_number, + FileDescriptorProto* output) { + const Descriptor* extendee = pool_.FindMessageTypeByName(containing_type); + if (extendee == NULL) return false; + + const FieldDescriptor* extension = + pool_.FindExtensionByNumber(extendee, field_number); + if (extension == NULL) return false; + + output->Clear(); + extension->file()->CopyTo(output); + return true; +} + +// =================================================================== + +MergedDescriptorDatabase::MergedDescriptorDatabase( + DescriptorDatabase* source1, + DescriptorDatabase* source2) { + sources_.push_back(source1); + sources_.push_back(source2); +} +MergedDescriptorDatabase::MergedDescriptorDatabase( + const vector& sources) + : sources_(sources) {} +MergedDescriptorDatabase::~MergedDescriptorDatabase() {} + +bool MergedDescriptorDatabase::FindFileByName( + const string& filename, + FileDescriptorProto* output) { + for (int i = 0; i < sources_.size(); i++) { + if (sources_[i]->FindFileByName(filename, output)) { + return true; + } + } + return false; +} + +bool MergedDescriptorDatabase::FindFileContainingSymbol( + const string& symbol_name, + FileDescriptorProto* output) { + for (int i = 0; i < sources_.size(); i++) { + if (sources_[i]->FindFileContainingSymbol(symbol_name, output)) { + // The symbol was found in source i. However, if one of the previous + // sources defines a file with the same name (which presumably doesn't + // contain the symbol, since it wasn't found in that source), then we + // must hide it from the caller. + FileDescriptorProto temp; + for (int j = 0; j < i; j++) { + if (sources_[j]->FindFileByName(output->name(), &temp)) { + // Found conflicting file in a previous source. + return false; + } + } + return true; + } + } + return false; +} + +bool MergedDescriptorDatabase::FindFileContainingExtension( + const string& containing_type, + int field_number, + FileDescriptorProto* output) { + for (int i = 0; i < sources_.size(); i++) { + if (sources_[i]->FindFileContainingExtension( + containing_type, field_number, output)) { + // The symbol was found in source i. However, if one of the previous + // sources defines a file with the same name (which presumably doesn't + // contain the symbol, since it wasn't found in that source), then we + // must hide it from the caller. + FileDescriptorProto temp; + for (int j = 0; j < i; j++) { + if (sources_[j]->FindFileByName(output->name(), &temp)) { + // Found conflicting file in a previous source. + return false; + } + } + return true; + } + } + return false; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor_database.h b/src/google/protobuf/descriptor_database.h new file mode 100644 index 00000000..d629da4c --- /dev/null +++ b/src/google/protobuf/descriptor_database.h @@ -0,0 +1,183 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Interface for manipulating databases of descriptors. + +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__ +#define GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__ + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +// Abstract interface for a database of descriptors. +// +// This is useful if you want to create a DescriptorPool which loads +// descriptors on-demand from some sort of large database. If the database +// is large, it may be inefficient to enumerate every .proto file inside it +// calling DescriptorPool::BuildFile() for each one. Instead, a DescriptorPool +// can be created which wraps a DescriptorDatabase and only builds particular +// descriptors when they are needed. +class LIBPROTOBUF_EXPORT DescriptorDatabase { + public: + inline DescriptorDatabase() {} + virtual ~DescriptorDatabase(); + + // Find a file by file name. Fills in in *output and returns true if found. + // Otherwise, returns false, leaving the contents of *output undefined. + virtual bool FindFileByName(const string& filename, + FileDescriptorProto* output) = 0; + + // Find the file that declares the given fully-qualified symbol name. + // If found, fills in *output and returns true, otherwise returns false + // and leaves *output undefined. + virtual bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) = 0; + + // Find the file which defines an extension extending the given message type + // with the given field number. If found, fills in *output and returns true, + // otherwise returns false and leaves *output undefined. containing_type + // must be a fully-qualified type name. + virtual bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase); +}; + +// A DescriptorDatabase into which you can insert files manually. +// +// FindFileContainingSymbol() is fully-implemented. When you add a file, its +// symbols will be indexed for this purpose. +// +// FindFileContainingExtension() is mostly-implemented. It works if and only +// if the original FieldDescriptorProto defining the extension has a +// fully-qualified type name in its "extendee" field (i.e. starts with a '.'). +// If the extendee is a relative name, SimpleDescriptorDatabase will not +// attempt to resolve the type, so it will not know what type the extension is +// extending. Therefore, calling FindFileContainingExtension() with the +// extension's containing type will never actually find that extension. Note +// that this is an unlikely problem, as all FileDescriptorProtos created by the +// protocol compiler (as well as ones created by calling +// FileDescriptor::CopyTo()) will always use fully-qualified names for all +// types. You only need to worry if you are constructing FileDescriptorProtos +// yourself, or are calling compiler::Parser directly. +class LIBPROTOBUF_EXPORT SimpleDescriptorDatabase : public DescriptorDatabase { + public: + SimpleDescriptorDatabase(); + ~SimpleDescriptorDatabase(); + + // Adds the FileDescriptorProto to the database, making a copy. The object + // can be deleted after Add() returns. + void Add(const FileDescriptorProto& file); + + // Adds the FileDescriptorProto to the database and takes ownership of it. + void AddAndOwn(const FileDescriptorProto* file); + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + // Helpers to recursively add particular descriptors and all their contents + // to the by-symbol and by-extension tables. + void AddMessage(const string& path, + const DescriptorProto& message_type, + const FileDescriptorProto* file); + void AddField(const string& path, + const FieldDescriptorProto& field, + const FileDescriptorProto* file); + void AddEnum(const string& path, + const EnumDescriptorProto& enum_type, + const FileDescriptorProto* file); + void AddService(const string& path, + const ServiceDescriptorProto& service, + const FileDescriptorProto* file); + + vector files_to_delete_; + map files_by_name_; + map files_by_symbol_; + map, const FileDescriptorProto*> files_by_extension_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SimpleDescriptorDatabase); +}; + +// A DescriptorDatabase that fetches files from a given pool. +class LIBPROTOBUF_EXPORT DescriptorPoolDatabase : public DescriptorDatabase { + public: + DescriptorPoolDatabase(const DescriptorPool& pool); + ~DescriptorPoolDatabase(); + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + const DescriptorPool& pool_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPoolDatabase); +}; + +// A DescriptorDatabase that wraps two or more others. It first searches the +// first database and, if that fails, tries the second, and so on. +class LIBPROTOBUF_EXPORT MergedDescriptorDatabase : public DescriptorDatabase { + public: + // Merge just two databases. The sources remain property of the caller. + MergedDescriptorDatabase(DescriptorDatabase* source1, + DescriptorDatabase* source2); + // Merge more than two databases. The sources remain property of the caller. + // The vector may be deleted after the constructor returns but the + // DescriptorDatabases need to stick around. + MergedDescriptorDatabase(const vector& sources); + ~MergedDescriptorDatabase(); + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + vector sources_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MergedDescriptorDatabase); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__ diff --git a/src/google/protobuf/descriptor_database_unittest.cc b/src/google/protobuf/descriptor_database_unittest.cc new file mode 100644 index 00000000..cbbf5927 --- /dev/null +++ b/src/google/protobuf/descriptor_database_unittest.cc @@ -0,0 +1,601 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file makes extensive use of RFC 3092. :) + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace { + +static bool AddToPool(DescriptorPool* pool, const char* file_text) { + FileDescriptorProto file_proto; + if (!TextFormat::ParseFromString(file_text, &file_proto)) return false; + if (pool->BuildFile(file_proto) == NULL) return false; + return true; +} + +static void AddToDatabase(SimpleDescriptorDatabase* database, + const char* file_text) { + FileDescriptorProto file_proto; + EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + database->Add(file_proto); +} + +static void ExpectContainsType(const FileDescriptorProto& proto, + const string& type_name) { + for (int i = 0; i < proto.message_type_size(); i++) { + if (proto.message_type(i).name() == type_name) return; + } + ADD_FAILURE() << "\"" << proto.name() + << "\" did not contain expected type \"" + << type_name << "\"."; +} + +// =================================================================== + +TEST(SimpleDescriptorDatabaseTest, FindFileByName) { + SimpleDescriptorDatabase database; + AddToDatabase(&database, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" }"); + AddToDatabase(&database, + "name: \"bar.proto\" " + "message_type { name:\"Bar\" }"); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("foo.proto", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("bar.proto", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // Fails to find undefined files. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileByName("baz.proto", &file)); + } +} + +TEST(SimpleDescriptorDatabaseTest, FindFileContainingSymbol) { + SimpleDescriptorDatabase database; + AddToDatabase(&database, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " field { name:\"qux\" }" + " nested_type { name: \"Grault\" } " + " enum_type { name: \"Garply\" } " + "} " + "enum_type { " + " name: \"Waldo\" " + " value { name:\"FRED\" } " + "} " + "extension { name: \"plugh\" } " + "service { " + " name: \"Xyzzy\" " + " method { name: \"Thud\" } " + "}" + ); + AddToDatabase(&database, + "name: \"bar.proto\" " + "package: \"corge\" " + "message_type { name: \"Bar\" }"); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find fields. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.qux", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find nested types. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.Grault", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find nested enums. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.Garply", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find enum types. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Waldo", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find enum values. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Waldo.FRED", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find extensions. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("plugh", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find services. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Xyzzy", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find methods. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Xyzzy.Thud", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find things in packages. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("corge.Bar", &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Fails to find undefined symbols. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Baz", &file)); + } + + { + // Names must be fully-qualified. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Bar", &file)); + } +} + +TEST(SimpleDescriptorDatabaseTest, FindFileContainingExtension) { + SimpleDescriptorDatabase database; + AddToDatabase(&database, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " extension_range { start: 1 end: 1000 } " + " extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 " + " extendee: \".Foo\" }" + "}"); + AddToDatabase(&database, + "name: \"bar.proto\" " + "package: \"corge\" " + "dependency: \"foo.proto\" " + "message_type { " + " name: \"Bar\" " + " extension_range { start: 1 end: 1000 } " + "} " + "extension { name:\"grault\" extendee: \".Foo\" number:32 } " + "extension { name:\"garply\" extendee: \".corge.Bar\" number:70 } " + "extension { name:\"waldo\" extendee: \"Bar\" number:56 } "); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 5, &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 32, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can find extensions for qualified type names. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("corge.Bar", 70, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can't find extensions whose extendee was not fully-qualified in the + // FileDescriptorProto. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Bar", 56, &file)); + EXPECT_FALSE(database.FindFileContainingExtension("corge.Bar", 56, &file)); + } + + { + // Can't find non-existent extension numbers. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Foo", 12, &file)); + } + + { + // Can't find extensions for non-existent types. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("NoSuchType", 5, &file)); + } + + { + // Can't find extensions for unqualified type names. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Bar", 70, &file)); + } +} + +// =================================================================== + +TEST(DescriptorPoolDatabaseTest, FindFileByName) { + DescriptorPool pool; + ASSERT_TRUE(AddToPool(&pool, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" }")); + ASSERT_TRUE(AddToPool(&pool, + "name: \"bar.proto\" " + "message_type { name:\"Bar\" }")); + + DescriptorPoolDatabase database(pool); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("foo.proto", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("bar.proto", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // Fails to find undefined files. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileByName("baz.proto", &file)); + } +} + +TEST(DescriptorPoolDatabaseTest, FindFileContainingSymbol) { + DescriptorPool pool; + ASSERT_TRUE(AddToPool(&pool, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " field { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" + "}")); + ASSERT_TRUE(AddToPool(&pool, + "name: \"bar.proto\" " + "package: \"corge\" " + "message_type { name: \"Bar\" }")); + + DescriptorPoolDatabase database(pool); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find fields. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.qux", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find things in packages. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("corge.Bar", &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Fails to find undefined symbols. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Baz", &file)); + } + + { + // Names must be fully-qualified. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Bar", &file)); + } +} + +TEST(DescriptorPoolDatabaseTest, FindFileContainingExtension) { + DescriptorPool pool; + ASSERT_TRUE(AddToPool(&pool, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " extension_range { start: 1 end: 1000 } " + " extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 " + " extendee: \"Foo\" }" + "}")); + ASSERT_TRUE(AddToPool(&pool, + "name: \"bar.proto\" " + "package: \"corge\" " + "dependency: \"foo.proto\" " + "message_type { " + " name: \"Bar\" " + " extension_range { start: 1 end: 1000 } " + "} " + "extension { name:\"grault\" label:LABEL_OPTIONAL type:TYPE_BOOL number:32 " + " extendee: \"Foo\" } " + "extension { name:\"garply\" label:LABEL_OPTIONAL type:TYPE_BOOL number:70 " + " extendee: \"Bar\" } ")); + + DescriptorPoolDatabase database(pool); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 5, &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 32, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can find extensions for qualified type names.. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("corge.Bar", 70, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can't find non-existent extension numbers. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Foo", 12, &file)); + } + + { + // Can't find extensions for non-existent types. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("NoSuchType", 5, &file)); + } + + { + // Can't find extensions for unqualified type names. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Bar", 70, &file)); + } +} + +// =================================================================== + +class MergedDescriptorDatabaseTest : public testing::Test { + protected: + MergedDescriptorDatabaseTest() + : forward_merged_(&database1_, &database2_), + reverse_merged_(&database2_, &database1_) {} + + virtual void SetUp() { + AddToDatabase(&database1_, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" extension_range { start: 1 end: 100 } } " + "extension { name:\"foo_ext\" extendee: \".Foo\" number:3 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + AddToDatabase(&database2_, + "name: \"bar.proto\" " + "message_type { name:\"Bar\" extension_range { start: 1 end: 100 } } " + "extension { name:\"bar_ext\" extendee: \".Bar\" number:5 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + + // baz.proto exists in both pools, with different definitions. + AddToDatabase(&database1_, + "name: \"baz.proto\" " + "message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } " + "message_type { name:\"FromPool1\" } " + "extension { name:\"baz_ext\" extendee: \".Baz\" number:12 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } " + "extension { name:\"database1_only_ext\" extendee: \".Baz\" number:13 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + AddToDatabase(&database2_, + "name: \"baz.proto\" " + "message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } " + "message_type { name:\"FromPool2\" } " + "extension { name:\"baz_ext\" extendee: \".Baz\" number:12 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + } + + SimpleDescriptorDatabase database1_; + SimpleDescriptorDatabase database2_; + + MergedDescriptorDatabase forward_merged_; + MergedDescriptorDatabase reverse_merged_; +}; + +TEST_F(MergedDescriptorDatabaseTest, FindFileByName) { + { + // Can find file that is only in database1_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileByName("foo.proto", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + // Can find file that is only in database2_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileByName("bar.proto", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // In forward_merged_, database1_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileByName("baz.proto", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool1"); + } + + { + // In reverse_merged_, database2_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(reverse_merged_.FindFileByName("baz.proto", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool2"); + } + + { + // Can't find non-existent file. + FileDescriptorProto file; + EXPECT_FALSE(forward_merged_.FindFileByName("no_such.proto", &file)); + } +} + +TEST_F(MergedDescriptorDatabaseTest, FindFileContainingSymbol) { + { + // Can find file that is only in database1_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Foo", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + // Can find file that is only in database2_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Bar", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // In forward_merged_, database1_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Baz", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool1"); + } + + { + // In reverse_merged_, database2_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(reverse_merged_.FindFileContainingSymbol("Baz", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool2"); + } + + { + // FromPool1 only shows up in forward_merged_ because it is masked by + // database2_'s baz.proto in reverse_merged_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("FromPool1", &file)); + EXPECT_FALSE(reverse_merged_.FindFileContainingSymbol("FromPool1", &file)); + } + + { + // Can't find non-existent symbol. + FileDescriptorProto file; + EXPECT_FALSE( + forward_merged_.FindFileContainingSymbol("NoSuchType", &file)); + } +} + +TEST_F(MergedDescriptorDatabaseTest, FindFileContainingExtension) { + { + // Can find file that is only in database1_. + FileDescriptorProto file; + EXPECT_TRUE( + forward_merged_.FindFileContainingExtension("Foo", 3, &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + // Can find file that is only in database2_. + FileDescriptorProto file; + EXPECT_TRUE( + forward_merged_.FindFileContainingExtension("Bar", 5, &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // In forward_merged_, database1_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE( + forward_merged_.FindFileContainingExtension("Baz", 12, &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool1"); + } + + { + // In reverse_merged_, database2_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE( + reverse_merged_.FindFileContainingExtension("Baz", 12, &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool2"); + } + + { + // Baz's extension 13 only shows up in forward_merged_ because it is + // masked by database2_'s baz.proto in reverse_merged_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingExtension("Baz", 13, &file)); + EXPECT_FALSE(reverse_merged_.FindFileContainingExtension("Baz", 13, &file)); + } + + { + // Can't find non-existent extension. + FileDescriptorProto file; + EXPECT_FALSE( + forward_merged_.FindFileContainingExtension("Foo", 6, &file)); + } +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc new file mode 100644 index 00000000..18397a66 --- /dev/null +++ b/src/google/protobuf/descriptor_unittest.cc @@ -0,0 +1,2634 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file makes extensive use of RFC 3092. :) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace { + +// Some helpers to make assembling descriptors faster. +DescriptorProto* AddMessage(FileDescriptorProto* file, const string& name) { + DescriptorProto* result = file->add_message_type(); + result->set_name(name); + return result; +} + +DescriptorProto* AddNestedMessage(DescriptorProto* parent, const string& name) { + DescriptorProto* result = parent->add_nested_type(); + result->set_name(name); + return result; +} + +EnumDescriptorProto* AddEnum(FileDescriptorProto* file, const string& name) { + EnumDescriptorProto* result = file->add_enum_type(); + result->set_name(name); + return result; +} + +EnumDescriptorProto* AddNestedEnum(DescriptorProto* parent, + const string& name) { + EnumDescriptorProto* result = parent->add_enum_type(); + result->set_name(name); + return result; +} + +ServiceDescriptorProto* AddService(FileDescriptorProto* file, + const string& name) { + ServiceDescriptorProto* result = file->add_service(); + result->set_name(name); + return result; +} + +FieldDescriptorProto* AddField(DescriptorProto* parent, + const string& name, int number, + FieldDescriptorProto::Label label, + FieldDescriptorProto::Type type) { + FieldDescriptorProto* result = parent->add_field(); + result->set_name(name); + result->set_number(number); + result->set_label(label); + result->set_type(type); + return result; +} + +FieldDescriptorProto* AddExtension(FileDescriptorProto* file, + const string& extendee, + const string& name, int number, + FieldDescriptorProto::Label label, + FieldDescriptorProto::Type type) { + FieldDescriptorProto* result = file->add_extension(); + result->set_name(name); + result->set_number(number); + result->set_label(label); + result->set_type(type); + result->set_extendee(extendee); + return result; +} + +FieldDescriptorProto* AddNestedExtension(DescriptorProto* parent, + const string& extendee, + const string& name, int number, + FieldDescriptorProto::Label label, + FieldDescriptorProto::Type type) { + FieldDescriptorProto* result = parent->add_extension(); + result->set_name(name); + result->set_number(number); + result->set_label(label); + result->set_type(type); + result->set_extendee(extendee); + return result; +} + +DescriptorProto::ExtensionRange* AddExtensionRange(DescriptorProto* parent, + int start, int end) { + DescriptorProto::ExtensionRange* result = parent->add_extension_range(); + result->set_start(start); + result->set_end(end); + return result; +} + +EnumValueDescriptorProto* AddEnumValue(EnumDescriptorProto* enum_proto, + const string& name, int number) { + EnumValueDescriptorProto* result = enum_proto->add_value(); + result->set_name(name); + result->set_number(number); + return result; +} + +MethodDescriptorProto* AddMethod(ServiceDescriptorProto* service, + const string& name, + const string& input_type, + const string& output_type) { + MethodDescriptorProto* result = service->add_method(); + result->set_name(name); + result->set_input_type(input_type); + result->set_output_type(output_type); + return result; +} + +// Empty enums technically aren't allowed. We need to insert a dummy value +// into them. +void AddEmptyEnum(FileDescriptorProto* file, const string& name) { + AddEnumValue(AddEnum(file, name), name + "_DUMMY", 1); +} + +// =================================================================== + +// Test simple files. +class FileDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // message FooMessage { extensions 1; } + // enum FooEnum {FOO_ENUM_VALUE = 1;} + // service FooService {} + // extend FooMessage { optional int32 foo_extension = 1; } + // + // // in "bar.proto" + // package bar_package; + // message BarMessage { extensions 1; } + // enum BarEnum {BAR_ENUM_VALUE = 1;} + // service BarService {} + // extend BarMessage { optional int32 bar_extension = 1; } + // + // Also, we have an empty file "baz.proto". This file's purpose is to + // make sure that even though it has the same package as foo.proto, + // searching it for members of foo.proto won't work. + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + AddExtensionRange(AddMessage(&foo_file, "FooMessage"), 1, 2); + AddEnumValue(AddEnum(&foo_file, "FooEnum"), "FOO_ENUM_VALUE", 1); + AddService(&foo_file, "FooService"); + AddExtension(&foo_file, "FooMessage", "foo_extension", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("bar_package"); + bar_file.add_dependency("foo.proto"); + AddExtensionRange(AddMessage(&bar_file, "BarMessage"), 1, 2); + AddEnumValue(AddEnum(&bar_file, "BarEnum"), "BAR_ENUM_VALUE", 1); + AddService(&bar_file, "BarService"); + AddExtension(&bar_file, "bar_package.BarMessage", "bar_extension", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + FileDescriptorProto baz_file; + baz_file.set_name("baz.proto"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + baz_file_ = pool_.BuildFile(baz_file); + ASSERT_TRUE(baz_file_ != NULL); + + ASSERT_EQ(1, foo_file_->message_type_count()); + foo_message_ = foo_file_->message_type(0); + ASSERT_EQ(1, foo_file_->enum_type_count()); + foo_enum_ = foo_file_->enum_type(0); + ASSERT_EQ(1, foo_enum_->value_count()); + foo_enum_value_ = foo_enum_->value(0); + ASSERT_EQ(1, foo_file_->service_count()); + foo_service_ = foo_file_->service(0); + ASSERT_EQ(1, foo_file_->extension_count()); + foo_extension_ = foo_file_->extension(0); + + ASSERT_EQ(1, bar_file_->message_type_count()); + bar_message_ = bar_file_->message_type(0); + ASSERT_EQ(1, bar_file_->enum_type_count()); + bar_enum_ = bar_file_->enum_type(0); + ASSERT_EQ(1, bar_enum_->value_count()); + bar_enum_value_ = bar_enum_->value(0); + ASSERT_EQ(1, bar_file_->service_count()); + bar_service_ = bar_file_->service(0); + ASSERT_EQ(1, bar_file_->extension_count()); + bar_extension_ = bar_file_->extension(0); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + const FileDescriptor* baz_file_; + + const Descriptor* foo_message_; + const EnumDescriptor* foo_enum_; + const EnumValueDescriptor* foo_enum_value_; + const ServiceDescriptor* foo_service_; + const FieldDescriptor* foo_extension_; + + const Descriptor* bar_message_; + const EnumDescriptor* bar_enum_; + const EnumValueDescriptor* bar_enum_value_; + const ServiceDescriptor* bar_service_; + const FieldDescriptor* bar_extension_; +}; + +TEST_F(FileDescriptorTest, Name) { + EXPECT_EQ("foo.proto", foo_file_->name()); + EXPECT_EQ("bar.proto", bar_file_->name()); + EXPECT_EQ("baz.proto", baz_file_->name()); +} + +TEST_F(FileDescriptorTest, Package) { + EXPECT_EQ("", foo_file_->package()); + EXPECT_EQ("bar_package", bar_file_->package()); +} + +TEST_F(FileDescriptorTest, Dependencies) { + EXPECT_EQ(0, foo_file_->dependency_count()); + EXPECT_EQ(1, bar_file_->dependency_count()); + EXPECT_EQ(foo_file_, bar_file_->dependency(0)); +} + +TEST_F(FileDescriptorTest, FindMessageTypeByName) { + EXPECT_EQ(foo_message_, foo_file_->FindMessageTypeByName("FooMessage")); + EXPECT_EQ(bar_message_, bar_file_->FindMessageTypeByName("BarMessage")); + + EXPECT_TRUE(foo_file_->FindMessageTypeByName("BarMessage") == NULL); + EXPECT_TRUE(bar_file_->FindMessageTypeByName("FooMessage") == NULL); + EXPECT_TRUE(baz_file_->FindMessageTypeByName("FooMessage") == NULL); + + EXPECT_TRUE(foo_file_->FindMessageTypeByName("NoSuchMessage") == NULL); + EXPECT_TRUE(foo_file_->FindMessageTypeByName("FooEnum") == NULL); +} + +TEST_F(FileDescriptorTest, FindEnumTypeByName) { + EXPECT_EQ(foo_enum_, foo_file_->FindEnumTypeByName("FooEnum")); + EXPECT_EQ(bar_enum_, bar_file_->FindEnumTypeByName("BarEnum")); + + EXPECT_TRUE(foo_file_->FindEnumTypeByName("BarEnum") == NULL); + EXPECT_TRUE(bar_file_->FindEnumTypeByName("FooEnum") == NULL); + EXPECT_TRUE(baz_file_->FindEnumTypeByName("FooEnum") == NULL); + + EXPECT_TRUE(foo_file_->FindEnumTypeByName("NoSuchEnum") == NULL); + EXPECT_TRUE(foo_file_->FindEnumTypeByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindEnumValueByName) { + EXPECT_EQ(foo_enum_value_, foo_file_->FindEnumValueByName("FOO_ENUM_VALUE")); + EXPECT_EQ(bar_enum_value_, bar_file_->FindEnumValueByName("BAR_ENUM_VALUE")); + + EXPECT_TRUE(foo_file_->FindEnumValueByName("BAR_ENUM_VALUE") == NULL); + EXPECT_TRUE(bar_file_->FindEnumValueByName("FOO_ENUM_VALUE") == NULL); + EXPECT_TRUE(baz_file_->FindEnumValueByName("FOO_ENUM_VALUE") == NULL); + + EXPECT_TRUE(foo_file_->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(foo_file_->FindEnumValueByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindServiceByName) { + EXPECT_EQ(foo_service_, foo_file_->FindServiceByName("FooService")); + EXPECT_EQ(bar_service_, bar_file_->FindServiceByName("BarService")); + + EXPECT_TRUE(foo_file_->FindServiceByName("BarService") == NULL); + EXPECT_TRUE(bar_file_->FindServiceByName("FooService") == NULL); + EXPECT_TRUE(baz_file_->FindServiceByName("FooService") == NULL); + + EXPECT_TRUE(foo_file_->FindServiceByName("NoSuchService") == NULL); + EXPECT_TRUE(foo_file_->FindServiceByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindExtensionByName) { + EXPECT_EQ(foo_extension_, foo_file_->FindExtensionByName("foo_extension")); + EXPECT_EQ(bar_extension_, bar_file_->FindExtensionByName("bar_extension")); + + EXPECT_TRUE(foo_file_->FindExtensionByName("bar_extension") == NULL); + EXPECT_TRUE(bar_file_->FindExtensionByName("foo_extension") == NULL); + EXPECT_TRUE(baz_file_->FindExtensionByName("foo_extension") == NULL); + + EXPECT_TRUE(foo_file_->FindExtensionByName("no_such_extension") == NULL); + EXPECT_TRUE(foo_file_->FindExtensionByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindExtensionByNumber) { + EXPECT_EQ(foo_extension_, pool_.FindExtensionByNumber(foo_message_, 1)); + EXPECT_EQ(bar_extension_, pool_.FindExtensionByNumber(bar_message_, 1)); + + EXPECT_TRUE(pool_.FindExtensionByNumber(foo_message_, 2) == NULL); +} + +// =================================================================== + +// Test simple flat messages and fields. +class DescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // message TestForeign {} + // enum TestEnum {} + // + // message TestMessage { + // required string foo = 1; + // optional TestEnum bar = 6; + // repeated TestForeign baz = 500000000; + // optional group qux = 15 {} + // } + // + // // in "bar.proto" + // package corge.grault; + // message TestMessage2 { + // required string foo = 1; + // required string bar = 2; + // required string quux = 6; + // } + // + // We cheat and use TestForeign as the type for qux rather than create + // an actual nested type. + // + // Since all primitive types (including string) use the same building + // code, there's no need to test each one individually. + // + // TestMessage2 is primarily here to test FindFieldByName and friends. + // All messages created from the same DescriptorPool share the same lookup + // table, so we need to insure that they don't interfere. + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + AddMessage(&foo_file, "TestForeign"); + AddEmptyEnum(&foo_file, "TestEnum"); + + DescriptorProto* message = AddMessage(&foo_file, "TestMessage"); + AddField(message, "foo", 1, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + AddField(message, "bar", 6, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_ENUM) + ->set_type_name("TestEnum"); + AddField(message, "baz", 500000000, + FieldDescriptorProto::LABEL_REPEATED, + FieldDescriptorProto::TYPE_MESSAGE) + ->set_type_name("TestForeign"); + AddField(message, "qux", 15, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_GROUP) + ->set_type_name("TestForeign"); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + + DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2"); + AddField(message2, "foo", 1, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + AddField(message2, "bar", 2, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + AddField(message2, "quux", 6, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(1, foo_file_->enum_type_count()); + enum_ = foo_file_->enum_type(0); + + ASSERT_EQ(2, foo_file_->message_type_count()); + foreign_ = foo_file_->message_type(0); + message_ = foo_file_->message_type(1); + + ASSERT_EQ(4, message_->field_count()); + foo_ = message_->field(0); + bar_ = message_->field(1); + baz_ = message_->field(2); + qux_ = message_->field(3); + + ASSERT_EQ(1, bar_file_->message_type_count()); + message2_ = bar_file_->message_type(0); + + ASSERT_EQ(3, message2_->field_count()); + foo2_ = message2_->field(0); + bar2_ = message2_->field(1); + quux2_ = message2_->field(2); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const Descriptor* message_; + const Descriptor* message2_; + const Descriptor* foreign_; + const EnumDescriptor* enum_; + + const FieldDescriptor* foo_; + const FieldDescriptor* bar_; + const FieldDescriptor* baz_; + const FieldDescriptor* qux_; + + const FieldDescriptor* foo2_; + const FieldDescriptor* bar2_; + const FieldDescriptor* quux2_; +}; + +TEST_F(DescriptorTest, Name) { + EXPECT_EQ("TestMessage", message_->name()); + EXPECT_EQ("TestMessage", message_->full_name()); + EXPECT_EQ(foo_file_, message_->file()); + + EXPECT_EQ("TestMessage2", message2_->name()); + EXPECT_EQ("corge.grault.TestMessage2", message2_->full_name()); + EXPECT_EQ(bar_file_, message2_->file()); +} + +TEST_F(DescriptorTest, ContainingType) { + EXPECT_TRUE(message_->containing_type() == NULL); + EXPECT_TRUE(message2_->containing_type() == NULL); +} + +TEST_F(DescriptorTest, FieldsByIndex) { + ASSERT_EQ(4, message_->field_count()); + EXPECT_EQ(foo_, message_->field(0)); + EXPECT_EQ(bar_, message_->field(1)); + EXPECT_EQ(baz_, message_->field(2)); + EXPECT_EQ(qux_, message_->field(3)); +} + +TEST_F(DescriptorTest, FindFieldByName) { + // All messages in the same DescriptorPool share a single lookup table for + // fields. So, in addition to testing that FindFieldByName finds the fields + // of the message, we need to test that it does *not* find the fields of + // *other* messages. + + EXPECT_EQ(foo_, message_->FindFieldByName("foo")); + EXPECT_EQ(bar_, message_->FindFieldByName("bar")); + EXPECT_EQ(baz_, message_->FindFieldByName("baz")); + EXPECT_EQ(qux_, message_->FindFieldByName("qux")); + EXPECT_TRUE(message_->FindFieldByName("no_such_field") == NULL); + EXPECT_TRUE(message_->FindFieldByName("quux") == NULL); + + EXPECT_EQ(foo2_ , message2_->FindFieldByName("foo" )); + EXPECT_EQ(bar2_ , message2_->FindFieldByName("bar" )); + EXPECT_EQ(quux2_, message2_->FindFieldByName("quux")); + EXPECT_TRUE(message2_->FindFieldByName("baz") == NULL); + EXPECT_TRUE(message2_->FindFieldByName("qux") == NULL); +} + +TEST_F(DescriptorTest, FindFieldByNumber) { + EXPECT_EQ(foo_, message_->FindFieldByNumber(1)); + EXPECT_EQ(bar_, message_->FindFieldByNumber(6)); + EXPECT_EQ(baz_, message_->FindFieldByNumber(500000000)); + EXPECT_EQ(qux_, message_->FindFieldByNumber(15)); + EXPECT_TRUE(message_->FindFieldByNumber(837592) == NULL); + EXPECT_TRUE(message_->FindFieldByNumber(2) == NULL); + + EXPECT_EQ(foo2_ , message2_->FindFieldByNumber(1)); + EXPECT_EQ(bar2_ , message2_->FindFieldByNumber(2)); + EXPECT_EQ(quux2_, message2_->FindFieldByNumber(6)); + EXPECT_TRUE(message2_->FindFieldByNumber(15) == NULL); + EXPECT_TRUE(message2_->FindFieldByNumber(500000000) == NULL); +} + +TEST_F(DescriptorTest, FieldName) { + EXPECT_EQ("foo", foo_->name()); + EXPECT_EQ("bar", bar_->name()); + EXPECT_EQ("baz", baz_->name()); + EXPECT_EQ("qux", qux_->name()); +} + +TEST_F(DescriptorTest, FieldFullName) { + EXPECT_EQ("TestMessage.foo", foo_->full_name()); + EXPECT_EQ("TestMessage.bar", bar_->full_name()); + EXPECT_EQ("TestMessage.baz", baz_->full_name()); + EXPECT_EQ("TestMessage.qux", qux_->full_name()); + + EXPECT_EQ("corge.grault.TestMessage2.foo", foo2_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.bar", bar2_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.quux", quux2_->full_name()); +} + +TEST_F(DescriptorTest, FieldFile) { + EXPECT_EQ(foo_file_, foo_->file()); + EXPECT_EQ(foo_file_, bar_->file()); + EXPECT_EQ(foo_file_, baz_->file()); + EXPECT_EQ(foo_file_, qux_->file()); + + EXPECT_EQ(bar_file_, foo2_->file()); + EXPECT_EQ(bar_file_, bar2_->file()); + EXPECT_EQ(bar_file_, quux2_->file()); +} + +TEST_F(DescriptorTest, FieldIndex) { + EXPECT_EQ(0, foo_->index()); + EXPECT_EQ(1, bar_->index()); + EXPECT_EQ(2, baz_->index()); + EXPECT_EQ(3, qux_->index()); +} + +TEST_F(DescriptorTest, FieldNumber) { + EXPECT_EQ( 1, foo_->number()); + EXPECT_EQ( 6, bar_->number()); + EXPECT_EQ(500000000, baz_->number()); + EXPECT_EQ( 15, qux_->number()); +} + +TEST_F(DescriptorTest, FieldType) { + EXPECT_EQ(FieldDescriptor::TYPE_STRING , foo_->type()); + EXPECT_EQ(FieldDescriptor::TYPE_ENUM , bar_->type()); + EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, baz_->type()); + EXPECT_EQ(FieldDescriptor::TYPE_GROUP , qux_->type()); +} + +TEST_F(DescriptorTest, FieldLabel) { + EXPECT_EQ(FieldDescriptor::LABEL_REQUIRED, foo_->label()); + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->label()); + EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, baz_->label()); + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, qux_->label()); + + EXPECT_TRUE (foo_->is_required()); + EXPECT_FALSE(foo_->is_optional()); + EXPECT_FALSE(foo_->is_repeated()); + + EXPECT_FALSE(bar_->is_required()); + EXPECT_TRUE (bar_->is_optional()); + EXPECT_FALSE(bar_->is_repeated()); + + EXPECT_FALSE(baz_->is_required()); + EXPECT_FALSE(baz_->is_optional()); + EXPECT_TRUE (baz_->is_repeated()); +} + +TEST_F(DescriptorTest, FieldHasDefault) { + EXPECT_FALSE(foo_->has_default_value()); + EXPECT_FALSE(bar_->has_default_value()); + EXPECT_FALSE(baz_->has_default_value()); + EXPECT_FALSE(qux_->has_default_value()); +} + +TEST_F(DescriptorTest, FieldContainingType) { + EXPECT_EQ(message_, foo_->containing_type()); + EXPECT_EQ(message_, bar_->containing_type()); + EXPECT_EQ(message_, baz_->containing_type()); + EXPECT_EQ(message_, qux_->containing_type()); + + EXPECT_EQ(message2_, foo2_ ->containing_type()); + EXPECT_EQ(message2_, bar2_ ->containing_type()); + EXPECT_EQ(message2_, quux2_->containing_type()); +} + +TEST_F(DescriptorTest, FieldMessageType) { + EXPECT_TRUE(foo_->message_type() == NULL); + EXPECT_TRUE(bar_->message_type() == NULL); + + EXPECT_EQ(foreign_, baz_->message_type()); + EXPECT_EQ(foreign_, qux_->message_type()); +} + +TEST_F(DescriptorTest, FieldEnumType) { + EXPECT_TRUE(foo_->enum_type() == NULL); + EXPECT_TRUE(baz_->enum_type() == NULL); + EXPECT_TRUE(qux_->enum_type() == NULL); + + EXPECT_EQ(enum_, bar_->enum_type()); +} + +// =================================================================== + +// Test enum descriptors. +class EnumDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // enum TestEnum { + // FOO = 1; + // BAR = 2; + // } + // + // // in "bar.proto" + // package corge.grault; + // enum TestEnum2 { + // FOO = 1; + // BAZ = 3; + // } + // + // TestEnum2 is primarily here to test FindValueByName and friends. + // All enums created from the same DescriptorPool share the same lookup + // table, so we need to insure that they don't interfere. + + // TestEnum + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + EnumDescriptorProto* enum_proto = AddEnum(&foo_file, "TestEnum"); + AddEnumValue(enum_proto, "FOO", 1); + AddEnumValue(enum_proto, "BAR", 2); + + // TestEnum2 + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + + EnumDescriptorProto* enum2_proto = AddEnum(&bar_file, "TestEnum2"); + AddEnumValue(enum2_proto, "FOO", 1); + AddEnumValue(enum2_proto, "BAZ", 3); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(1, foo_file_->enum_type_count()); + enum_ = foo_file_->enum_type(0); + + ASSERT_EQ(2, enum_->value_count()); + foo_ = enum_->value(0); + bar_ = enum_->value(1); + + ASSERT_EQ(1, bar_file_->enum_type_count()); + enum2_ = bar_file_->enum_type(0); + + ASSERT_EQ(2, enum2_->value_count()); + foo2_ = enum2_->value(0); + baz2_ = enum2_->value(1); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const EnumDescriptor* enum_; + const EnumDescriptor* enum2_; + + const EnumValueDescriptor* foo_; + const EnumValueDescriptor* bar_; + + const EnumValueDescriptor* foo2_; + const EnumValueDescriptor* baz2_; +}; + +TEST_F(EnumDescriptorTest, Name) { + EXPECT_EQ("TestEnum", enum_->name()); + EXPECT_EQ("TestEnum", enum_->full_name()); + EXPECT_EQ(foo_file_, enum_->file()); + + EXPECT_EQ("TestEnum2", enum2_->name()); + EXPECT_EQ("corge.grault.TestEnum2", enum2_->full_name()); + EXPECT_EQ(bar_file_, enum2_->file()); +} + +TEST_F(EnumDescriptorTest, ContainingType) { + EXPECT_TRUE(enum_->containing_type() == NULL); + EXPECT_TRUE(enum2_->containing_type() == NULL); +} + +TEST_F(EnumDescriptorTest, ValuesByIndex) { + ASSERT_EQ(2, enum_->value_count()); + EXPECT_EQ(foo_, enum_->value(0)); + EXPECT_EQ(bar_, enum_->value(1)); +} + +TEST_F(EnumDescriptorTest, FindValueByName) { + EXPECT_EQ(foo_ , enum_ ->FindValueByName("FOO")); + EXPECT_EQ(bar_ , enum_ ->FindValueByName("BAR")); + EXPECT_EQ(foo2_, enum2_->FindValueByName("FOO")); + EXPECT_EQ(baz2_, enum2_->FindValueByName("BAZ")); + + EXPECT_TRUE(enum_ ->FindValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(enum_ ->FindValueByName("BAZ" ) == NULL); + EXPECT_TRUE(enum2_->FindValueByName("BAR" ) == NULL); +} + +TEST_F(EnumDescriptorTest, FindValueByNumber) { + EXPECT_EQ(foo_ , enum_ ->FindValueByNumber(1)); + EXPECT_EQ(bar_ , enum_ ->FindValueByNumber(2)); + EXPECT_EQ(foo2_, enum2_->FindValueByNumber(1)); + EXPECT_EQ(baz2_, enum2_->FindValueByNumber(3)); + + EXPECT_TRUE(enum_ ->FindValueByNumber(416) == NULL); + EXPECT_TRUE(enum_ ->FindValueByNumber(3) == NULL); + EXPECT_TRUE(enum2_->FindValueByNumber(2) == NULL); +} + +TEST_F(EnumDescriptorTest, ValueName) { + EXPECT_EQ("FOO", foo_->name()); + EXPECT_EQ("BAR", bar_->name()); +} + +TEST_F(EnumDescriptorTest, ValueFullName) { + EXPECT_EQ("FOO", foo_->full_name()); + EXPECT_EQ("BAR", bar_->full_name()); + EXPECT_EQ("corge.grault.FOO", foo2_->full_name()); + EXPECT_EQ("corge.grault.BAZ", baz2_->full_name()); +} + +TEST_F(EnumDescriptorTest, ValueIndex) { + EXPECT_EQ(0, foo_->index()); + EXPECT_EQ(1, bar_->index()); +} + +TEST_F(EnumDescriptorTest, ValueNumber) { + EXPECT_EQ(1, foo_->number()); + EXPECT_EQ(2, bar_->number()); +} + +TEST_F(EnumDescriptorTest, ValueType) { + EXPECT_EQ(enum_ , foo_ ->type()); + EXPECT_EQ(enum_ , bar_ ->type()); + EXPECT_EQ(enum2_, foo2_->type()); + EXPECT_EQ(enum2_, baz2_->type()); +} + +// =================================================================== + +// Test service descriptors. +class ServiceDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following messages and service: + // // in "foo.proto" + // message FooRequest {} + // message FooResponse {} + // message BarRequest {} + // message BarResponse {} + // message BazRequest {} + // message BazResponse {} + // + // service TestService { + // rpc Foo(FooRequest) returns (FooResponse); + // rpc Bar(BarRequest) returns (BarResponse); + // } + // + // // in "bar.proto" + // package corge.grault + // service TestService2 { + // rpc Foo(FooRequest) returns (FooResponse); + // rpc Baz(BazRequest) returns (BazResponse); + // } + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + AddMessage(&foo_file, "FooRequest"); + AddMessage(&foo_file, "FooResponse"); + AddMessage(&foo_file, "BarRequest"); + AddMessage(&foo_file, "BarResponse"); + AddMessage(&foo_file, "BazRequest"); + AddMessage(&foo_file, "BazResponse"); + + ServiceDescriptorProto* service = AddService(&foo_file, "TestService"); + AddMethod(service, "Foo", "FooRequest", "FooResponse"); + AddMethod(service, "Bar", "BarRequest", "BarResponse"); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + bar_file.add_dependency("foo.proto"); + + ServiceDescriptorProto* service2 = AddService(&bar_file, "TestService2"); + AddMethod(service2, "Foo", "FooRequest", "FooResponse"); + AddMethod(service2, "Baz", "BazRequest", "BazResponse"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(6, foo_file_->message_type_count()); + foo_request_ = foo_file_->message_type(0); + foo_response_ = foo_file_->message_type(1); + bar_request_ = foo_file_->message_type(2); + bar_response_ = foo_file_->message_type(3); + baz_request_ = foo_file_->message_type(4); + baz_response_ = foo_file_->message_type(5); + + ASSERT_EQ(1, foo_file_->service_count()); + service_ = foo_file_->service(0); + + ASSERT_EQ(2, service_->method_count()); + foo_ = service_->method(0); + bar_ = service_->method(1); + + ASSERT_EQ(1, bar_file_->service_count()); + service2_ = bar_file_->service(0); + + ASSERT_EQ(2, service2_->method_count()); + foo2_ = service2_->method(0); + baz2_ = service2_->method(1); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const Descriptor* foo_request_; + const Descriptor* foo_response_; + const Descriptor* bar_request_; + const Descriptor* bar_response_; + const Descriptor* baz_request_; + const Descriptor* baz_response_; + + const ServiceDescriptor* service_; + const ServiceDescriptor* service2_; + + const MethodDescriptor* foo_; + const MethodDescriptor* bar_; + + const MethodDescriptor* foo2_; + const MethodDescriptor* baz2_; +}; + +TEST_F(ServiceDescriptorTest, Name) { + EXPECT_EQ("TestService", service_->name()); + EXPECT_EQ("TestService", service_->full_name()); + EXPECT_EQ(foo_file_, service_->file()); + + EXPECT_EQ("TestService2", service2_->name()); + EXPECT_EQ("corge.grault.TestService2", service2_->full_name()); + EXPECT_EQ(bar_file_, service2_->file()); +} + +TEST_F(ServiceDescriptorTest, MethodsByIndex) { + ASSERT_EQ(2, service_->method_count()); + EXPECT_EQ(foo_, service_->method(0)); + EXPECT_EQ(bar_, service_->method(1)); +} + +TEST_F(ServiceDescriptorTest, FindMethodByName) { + EXPECT_EQ(foo_ , service_ ->FindMethodByName("Foo")); + EXPECT_EQ(bar_ , service_ ->FindMethodByName("Bar")); + EXPECT_EQ(foo2_, service2_->FindMethodByName("Foo")); + EXPECT_EQ(baz2_, service2_->FindMethodByName("Baz")); + + EXPECT_TRUE(service_ ->FindMethodByName("NoSuchMethod") == NULL); + EXPECT_TRUE(service_ ->FindMethodByName("Baz" ) == NULL); + EXPECT_TRUE(service2_->FindMethodByName("Bar" ) == NULL); +} + +TEST_F(ServiceDescriptorTest, MethodName) { + EXPECT_EQ("Foo", foo_->name()); + EXPECT_EQ("Bar", bar_->name()); +} + +TEST_F(ServiceDescriptorTest, MethodFullName) { + EXPECT_EQ("TestService.Foo", foo_->full_name()); + EXPECT_EQ("TestService.Bar", bar_->full_name()); + EXPECT_EQ("corge.grault.TestService2.Foo", foo2_->full_name()); + EXPECT_EQ("corge.grault.TestService2.Baz", baz2_->full_name()); +} + +TEST_F(ServiceDescriptorTest, MethodIndex) { + EXPECT_EQ(0, foo_->index()); + EXPECT_EQ(1, bar_->index()); +} + +TEST_F(ServiceDescriptorTest, MethodParent) { + EXPECT_EQ(service_, foo_->service()); + EXPECT_EQ(service_, bar_->service()); +} + +TEST_F(ServiceDescriptorTest, MethodInputType) { + EXPECT_EQ(foo_request_, foo_->input_type()); + EXPECT_EQ(bar_request_, bar_->input_type()); +} + +TEST_F(ServiceDescriptorTest, MethodOutputType) { + EXPECT_EQ(foo_response_, foo_->output_type()); + EXPECT_EQ(bar_response_, bar_->output_type()); +} + +// =================================================================== + +// Test nested types. +class NestedDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // message TestMessage { + // message Foo {} + // message Bar {} + // enum Baz { A = 1; } + // enum Qux { B = 1; } + // } + // + // // in "bar.proto" + // package corge.grault; + // message TestMessage2 { + // message Foo {} + // message Baz {} + // enum Qux { A = 1; } + // enum Quux { C = 1; } + // } + // + // TestMessage2 is primarily here to test FindNestedTypeByName and friends. + // All messages created from the same DescriptorPool share the same lookup + // table, so we need to insure that they don't interfere. + // + // We add enum values to the enums in order to test searching for enum + // values across a message's scope. + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + DescriptorProto* message = AddMessage(&foo_file, "TestMessage"); + AddNestedMessage(message, "Foo"); + AddNestedMessage(message, "Bar"); + EnumDescriptorProto* baz = AddNestedEnum(message, "Baz"); + AddEnumValue(baz, "A", 1); + EnumDescriptorProto* qux = AddNestedEnum(message, "Qux"); + AddEnumValue(qux, "B", 1); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + + DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2"); + AddNestedMessage(message2, "Foo"); + AddNestedMessage(message2, "Baz"); + EnumDescriptorProto* qux2 = AddNestedEnum(message2, "Qux"); + AddEnumValue(qux2, "A", 1); + EnumDescriptorProto* quux2 = AddNestedEnum(message2, "Quux"); + AddEnumValue(quux2, "C", 1); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(1, foo_file_->message_type_count()); + message_ = foo_file_->message_type(0); + + ASSERT_EQ(2, message_->nested_type_count()); + foo_ = message_->nested_type(0); + bar_ = message_->nested_type(1); + + ASSERT_EQ(2, message_->enum_type_count()); + baz_ = message_->enum_type(0); + qux_ = message_->enum_type(1); + + ASSERT_EQ(1, baz_->value_count()); + a_ = baz_->value(0); + ASSERT_EQ(1, qux_->value_count()); + b_ = qux_->value(0); + + ASSERT_EQ(1, bar_file_->message_type_count()); + message2_ = bar_file_->message_type(0); + + ASSERT_EQ(2, message2_->nested_type_count()); + foo2_ = message2_->nested_type(0); + baz2_ = message2_->nested_type(1); + + ASSERT_EQ(2, message2_->enum_type_count()); + qux2_ = message2_->enum_type(0); + quux2_ = message2_->enum_type(1); + + ASSERT_EQ(1, qux2_->value_count()); + a2_ = qux2_->value(0); + ASSERT_EQ(1, quux2_->value_count()); + c2_ = quux2_->value(0); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const Descriptor* message_; + const Descriptor* message2_; + + const Descriptor* foo_; + const Descriptor* bar_; + const EnumDescriptor* baz_; + const EnumDescriptor* qux_; + const EnumValueDescriptor* a_; + const EnumValueDescriptor* b_; + + const Descriptor* foo2_; + const Descriptor* baz2_; + const EnumDescriptor* qux2_; + const EnumDescriptor* quux2_; + const EnumValueDescriptor* a2_; + const EnumValueDescriptor* c2_; +}; + +TEST_F(NestedDescriptorTest, MessageName) { + EXPECT_EQ("Foo", foo_ ->name()); + EXPECT_EQ("Bar", bar_ ->name()); + EXPECT_EQ("Foo", foo2_->name()); + EXPECT_EQ("Baz", baz2_->name()); + + EXPECT_EQ("TestMessage.Foo", foo_->full_name()); + EXPECT_EQ("TestMessage.Bar", bar_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Foo", foo2_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Baz", baz2_->full_name()); +} + +TEST_F(NestedDescriptorTest, MessageContainingType) { + EXPECT_EQ(message_ , foo_ ->containing_type()); + EXPECT_EQ(message_ , bar_ ->containing_type()); + EXPECT_EQ(message2_, foo2_->containing_type()); + EXPECT_EQ(message2_, baz2_->containing_type()); +} + +TEST_F(NestedDescriptorTest, NestedMessagesByIndex) { + ASSERT_EQ(2, message_->nested_type_count()); + EXPECT_EQ(foo_, message_->nested_type(0)); + EXPECT_EQ(bar_, message_->nested_type(1)); +} + +TEST_F(NestedDescriptorTest, FindFieldByNameDoesntFindNestedTypes) { + EXPECT_TRUE(message_->FindFieldByName("Foo") == NULL); + EXPECT_TRUE(message_->FindFieldByName("Qux") == NULL); + EXPECT_TRUE(message_->FindExtensionByName("Foo") == NULL); + EXPECT_TRUE(message_->FindExtensionByName("Qux") == NULL); +} + +TEST_F(NestedDescriptorTest, FindNestedTypeByName) { + EXPECT_EQ(foo_ , message_ ->FindNestedTypeByName("Foo")); + EXPECT_EQ(bar_ , message_ ->FindNestedTypeByName("Bar")); + EXPECT_EQ(foo2_, message2_->FindNestedTypeByName("Foo")); + EXPECT_EQ(baz2_, message2_->FindNestedTypeByName("Baz")); + + EXPECT_TRUE(message_ ->FindNestedTypeByName("NoSuchType") == NULL); + EXPECT_TRUE(message_ ->FindNestedTypeByName("Baz" ) == NULL); + EXPECT_TRUE(message2_->FindNestedTypeByName("Bar" ) == NULL); + + EXPECT_TRUE(message_->FindNestedTypeByName("Qux") == NULL); +} + +TEST_F(NestedDescriptorTest, EnumName) { + EXPECT_EQ("Baz" , baz_ ->name()); + EXPECT_EQ("Qux" , qux_ ->name()); + EXPECT_EQ("Qux" , qux2_->name()); + EXPECT_EQ("Quux", quux2_->name()); + + EXPECT_EQ("TestMessage.Baz", baz_->full_name()); + EXPECT_EQ("TestMessage.Qux", qux_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Qux" , qux2_ ->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Quux", quux2_->full_name()); +} + +TEST_F(NestedDescriptorTest, EnumContainingType) { + EXPECT_EQ(message_ , baz_ ->containing_type()); + EXPECT_EQ(message_ , qux_ ->containing_type()); + EXPECT_EQ(message2_, qux2_ ->containing_type()); + EXPECT_EQ(message2_, quux2_->containing_type()); +} + +TEST_F(NestedDescriptorTest, NestedEnumsByIndex) { + ASSERT_EQ(2, message_->nested_type_count()); + EXPECT_EQ(foo_, message_->nested_type(0)); + EXPECT_EQ(bar_, message_->nested_type(1)); +} + +TEST_F(NestedDescriptorTest, FindEnumTypeByName) { + EXPECT_EQ(baz_ , message_ ->FindEnumTypeByName("Baz" )); + EXPECT_EQ(qux_ , message_ ->FindEnumTypeByName("Qux" )); + EXPECT_EQ(qux2_ , message2_->FindEnumTypeByName("Qux" )); + EXPECT_EQ(quux2_, message2_->FindEnumTypeByName("Quux")); + + EXPECT_TRUE(message_ ->FindEnumTypeByName("NoSuchType") == NULL); + EXPECT_TRUE(message_ ->FindEnumTypeByName("Quux" ) == NULL); + EXPECT_TRUE(message2_->FindEnumTypeByName("Baz" ) == NULL); + + EXPECT_TRUE(message_->FindEnumTypeByName("Foo") == NULL); +} + +TEST_F(NestedDescriptorTest, FindEnumValueByName) { + EXPECT_EQ(a_ , message_ ->FindEnumValueByName("A")); + EXPECT_EQ(b_ , message_ ->FindEnumValueByName("B")); + EXPECT_EQ(a2_, message2_->FindEnumValueByName("A")); + EXPECT_EQ(c2_, message2_->FindEnumValueByName("C")); + + EXPECT_TRUE(message_ ->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(message_ ->FindEnumValueByName("C" ) == NULL); + EXPECT_TRUE(message2_->FindEnumValueByName("B" ) == NULL); + + EXPECT_TRUE(message_->FindEnumValueByName("Foo") == NULL); +} + +// =================================================================== + +// Test extensions. +class ExtensionDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // enum Baz {} + // message Qux {} + // + // message Foo { + // extensions 10 to 19; + // extensions 30 to 39; + // } + // extends Foo with optional int32 foo_int32 = 10; + // extends Foo with repeated TestEnum foo_enum = 19; + // message Bar { + // extends Foo with optional Qux foo_message = 30; + // // (using Qux as the group type) + // extends Foo with repeated group foo_group = 39; + // } + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + AddEmptyEnum(&foo_file, "Baz"); + AddMessage(&foo_file, "Qux"); + + DescriptorProto* foo = AddMessage(&foo_file, "Foo"); + AddExtensionRange(foo, 10, 20); + AddExtensionRange(foo, 30, 40); + + AddExtension(&foo_file, "Foo", "foo_int32", 10, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddExtension(&foo_file, "Foo", "foo_enum", 19, + FieldDescriptorProto::LABEL_REPEATED, + FieldDescriptorProto::TYPE_ENUM) + ->set_type_name("Baz"); + + DescriptorProto* bar = AddMessage(&foo_file, "Bar"); + AddNestedExtension(bar, "Foo", "foo_message", 30, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_MESSAGE) + ->set_type_name("Qux"); + AddNestedExtension(bar, "Foo", "foo_group", 39, + FieldDescriptorProto::LABEL_REPEATED, + FieldDescriptorProto::TYPE_GROUP) + ->set_type_name("Qux"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + ASSERT_EQ(1, foo_file_->enum_type_count()); + baz_ = foo_file_->enum_type(0); + + ASSERT_EQ(3, foo_file_->message_type_count()); + qux_ = foo_file_->message_type(0); + foo_ = foo_file_->message_type(1); + bar_ = foo_file_->message_type(2); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + + const Descriptor* foo_; + const Descriptor* bar_; + const EnumDescriptor* baz_; + const Descriptor* qux_; +}; + +TEST_F(ExtensionDescriptorTest, ExtensionRanges) { + EXPECT_EQ(0, bar_->extension_range_count()); + ASSERT_EQ(2, foo_->extension_range_count()); + + EXPECT_EQ(10, foo_->extension_range(0)->start); + EXPECT_EQ(30, foo_->extension_range(1)->start); + + EXPECT_EQ(20, foo_->extension_range(0)->end); + EXPECT_EQ(40, foo_->extension_range(1)->end); +}; + +TEST_F(ExtensionDescriptorTest, Extensions) { + EXPECT_EQ(0, foo_->extension_count()); + ASSERT_EQ(2, foo_file_->extension_count()); + ASSERT_EQ(2, bar_->extension_count()); + + EXPECT_TRUE(foo_file_->extension(0)->is_extension()); + EXPECT_TRUE(foo_file_->extension(1)->is_extension()); + EXPECT_TRUE(bar_->extension(0)->is_extension()); + EXPECT_TRUE(bar_->extension(1)->is_extension()); + + EXPECT_EQ("foo_int32" , foo_file_->extension(0)->name()); + EXPECT_EQ("foo_enum" , foo_file_->extension(1)->name()); + EXPECT_EQ("foo_message", bar_->extension(0)->name()); + EXPECT_EQ("foo_group" , bar_->extension(1)->name()); + + EXPECT_EQ(10, foo_file_->extension(0)->number()); + EXPECT_EQ(19, foo_file_->extension(1)->number()); + EXPECT_EQ(30, bar_->extension(0)->number()); + EXPECT_EQ(39, bar_->extension(1)->number()); + + EXPECT_EQ(FieldDescriptor::TYPE_INT32 , foo_file_->extension(0)->type()); + EXPECT_EQ(FieldDescriptor::TYPE_ENUM , foo_file_->extension(1)->type()); + EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, bar_->extension(0)->type()); + EXPECT_EQ(FieldDescriptor::TYPE_GROUP , bar_->extension(1)->type()); + + EXPECT_EQ(baz_, foo_file_->extension(1)->enum_type()); + EXPECT_EQ(qux_, bar_->extension(0)->message_type()); + EXPECT_EQ(qux_, bar_->extension(1)->message_type()); + + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, foo_file_->extension(0)->label()); + EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, foo_file_->extension(1)->label()); + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->extension(0)->label()); + EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, bar_->extension(1)->label()); + + EXPECT_EQ(foo_, foo_file_->extension(0)->containing_type()); + EXPECT_EQ(foo_, foo_file_->extension(1)->containing_type()); + EXPECT_EQ(foo_, bar_->extension(0)->containing_type()); + EXPECT_EQ(foo_, bar_->extension(1)->containing_type()); + + EXPECT_TRUE(foo_file_->extension(0)->extension_scope() == NULL); + EXPECT_TRUE(foo_file_->extension(1)->extension_scope() == NULL); + EXPECT_EQ(bar_, bar_->extension(0)->extension_scope()); + EXPECT_EQ(bar_, bar_->extension(1)->extension_scope()); +}; + +TEST_F(ExtensionDescriptorTest, IsExtensionNumber) { + EXPECT_FALSE(foo_->IsExtensionNumber( 9)); + EXPECT_TRUE (foo_->IsExtensionNumber(10)); + EXPECT_TRUE (foo_->IsExtensionNumber(19)); + EXPECT_FALSE(foo_->IsExtensionNumber(20)); + EXPECT_FALSE(foo_->IsExtensionNumber(29)); + EXPECT_TRUE (foo_->IsExtensionNumber(30)); + EXPECT_TRUE (foo_->IsExtensionNumber(39)); + EXPECT_FALSE(foo_->IsExtensionNumber(40)); +} + +TEST_F(ExtensionDescriptorTest, FindExtensionByName) { + // Note that FileDescriptor::FindExtensionByName() is tested by + // FileDescriptorTest. + ASSERT_EQ(2, bar_->extension_count()); + + EXPECT_EQ(bar_->extension(0), bar_->FindExtensionByName("foo_message")); + EXPECT_EQ(bar_->extension(1), bar_->FindExtensionByName("foo_group" )); + + EXPECT_TRUE(bar_->FindExtensionByName("no_such_extension") == NULL); + EXPECT_TRUE(foo_->FindExtensionByName("foo_int32") == NULL); + EXPECT_TRUE(foo_->FindExtensionByName("foo_message") == NULL); +} + +// =================================================================== + +class MiscTest : public testing::Test { + protected: + // Function which makes a field of the given type just to find out what its + // cpp_type is. + FieldDescriptor::CppType GetCppTypeForFieldType(FieldDescriptor::Type type) { + FileDescriptorProto file_proto; + file_proto.set_name("foo.proto"); + AddEmptyEnum(&file_proto, "DummyEnum"); + + DescriptorProto* message = AddMessage(&file_proto, "TestMessage"); + FieldDescriptorProto* field = + AddField(message, "foo", 1, FieldDescriptorProto::LABEL_OPTIONAL, + static_cast(type)); + + if (type == FieldDescriptor::TYPE_MESSAGE || + type == FieldDescriptor::TYPE_GROUP) { + field->set_type_name("TestMessage"); + } else if (type == FieldDescriptor::TYPE_ENUM) { + field->set_type_name("DummyEnum"); + } + + // Build the descriptors and get the pointers. + DescriptorPool pool; + const FileDescriptor* file = pool.BuildFile(file_proto); + + if (file != NULL && + file->message_type_count() == 1 && + file->message_type(0)->field_count() == 1) { + return file->message_type(0)->field(0)->cpp_type(); + } else { + return static_cast(0); + } + } +}; + +TEST_F(MiscTest, CppTypes) { + // Test that CPP types are assigned correctly. + + typedef FieldDescriptor FD; // avoid ugly line wrapping + + EXPECT_EQ(FD::CPPTYPE_DOUBLE , GetCppTypeForFieldType(FD::TYPE_DOUBLE )); + EXPECT_EQ(FD::CPPTYPE_FLOAT , GetCppTypeForFieldType(FD::TYPE_FLOAT )); + EXPECT_EQ(FD::CPPTYPE_INT64 , GetCppTypeForFieldType(FD::TYPE_INT64 )); + EXPECT_EQ(FD::CPPTYPE_UINT64 , GetCppTypeForFieldType(FD::TYPE_UINT64 )); + EXPECT_EQ(FD::CPPTYPE_INT32 , GetCppTypeForFieldType(FD::TYPE_INT32 )); + EXPECT_EQ(FD::CPPTYPE_UINT64 , GetCppTypeForFieldType(FD::TYPE_FIXED64 )); + EXPECT_EQ(FD::CPPTYPE_UINT32 , GetCppTypeForFieldType(FD::TYPE_FIXED32 )); + EXPECT_EQ(FD::CPPTYPE_BOOL , GetCppTypeForFieldType(FD::TYPE_BOOL )); + EXPECT_EQ(FD::CPPTYPE_STRING , GetCppTypeForFieldType(FD::TYPE_STRING )); + EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_GROUP )); + EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_MESSAGE )); + EXPECT_EQ(FD::CPPTYPE_STRING , GetCppTypeForFieldType(FD::TYPE_BYTES )); + EXPECT_EQ(FD::CPPTYPE_UINT32 , GetCppTypeForFieldType(FD::TYPE_UINT32 )); + EXPECT_EQ(FD::CPPTYPE_ENUM , GetCppTypeForFieldType(FD::TYPE_ENUM )); + EXPECT_EQ(FD::CPPTYPE_INT32 , GetCppTypeForFieldType(FD::TYPE_SFIXED32)); + EXPECT_EQ(FD::CPPTYPE_INT64 , GetCppTypeForFieldType(FD::TYPE_SFIXED64)); + EXPECT_EQ(FD::CPPTYPE_INT32 , GetCppTypeForFieldType(FD::TYPE_SINT32 )); + EXPECT_EQ(FD::CPPTYPE_INT64 , GetCppTypeForFieldType(FD::TYPE_SINT64 )); +} + +TEST_F(MiscTest, DefaultValues) { + // Test that setting default values works. + FileDescriptorProto file_proto; + file_proto.set_name("foo.proto"); + + EnumDescriptorProto* enum_type_proto = AddEnum(&file_proto, "DummyEnum"); + AddEnumValue(enum_type_proto, "A", 1); + AddEnumValue(enum_type_proto, "B", 2); + + DescriptorProto* message_proto = AddMessage(&file_proto, "TestMessage"); + + typedef FieldDescriptorProto FD; // avoid ugly line wrapping + const FD::Label label = FD::LABEL_OPTIONAL; + + // Create fields of every CPP type with default values. + AddField(message_proto, "int32" , 1, label, FD::TYPE_INT32 ) + ->set_default_value("-1"); + AddField(message_proto, "int64" , 2, label, FD::TYPE_INT64 ) + ->set_default_value("-1000000000000"); + AddField(message_proto, "uint32", 3, label, FD::TYPE_UINT32) + ->set_default_value("42"); + AddField(message_proto, "uint64", 4, label, FD::TYPE_UINT64) + ->set_default_value("2000000000000"); + AddField(message_proto, "float" , 5, label, FD::TYPE_FLOAT ) + ->set_default_value("4.5"); + AddField(message_proto, "double", 6, label, FD::TYPE_DOUBLE) + ->set_default_value("10e100"); + AddField(message_proto, "bool" , 7, label, FD::TYPE_BOOL ) + ->set_default_value("true"); + AddField(message_proto, "string", 8, label, FD::TYPE_STRING) + ->set_default_value("hello"); + AddField(message_proto, "data" , 9, label, FD::TYPE_BYTES ) + ->set_default_value("\\001\\002\\003"); + + FieldDescriptorProto* enum_field = + AddField(message_proto, "enum", 10, label, FD::TYPE_ENUM); + enum_field->set_type_name("DummyEnum"); + enum_field->set_default_value("B"); + + // Strings are allowed to have empty defaults. (At one point, due to + // a bug, empty defaults for strings were rejected. Oops.) + AddField(message_proto, "empty_string", 11, label, FD::TYPE_STRING) + ->set_default_value(""); + + // Add a second set of fields with implicit defalut values. + AddField(message_proto, "implicit_int32" , 21, label, FD::TYPE_INT32 ); + AddField(message_proto, "implicit_int64" , 22, label, FD::TYPE_INT64 ); + AddField(message_proto, "implicit_uint32", 23, label, FD::TYPE_UINT32); + AddField(message_proto, "implicit_uint64", 24, label, FD::TYPE_UINT64); + AddField(message_proto, "implicit_float" , 25, label, FD::TYPE_FLOAT ); + AddField(message_proto, "implicit_double", 26, label, FD::TYPE_DOUBLE); + AddField(message_proto, "implicit_bool" , 27, label, FD::TYPE_BOOL ); + AddField(message_proto, "implicit_string", 28, label, FD::TYPE_STRING); + AddField(message_proto, "implicit_data" , 29, label, FD::TYPE_BYTES ); + AddField(message_proto, "implicit_enum" , 30, label, FD::TYPE_ENUM) + ->set_type_name("DummyEnum"); + + // Build it. + DescriptorPool pool; + const FileDescriptor* file = pool.BuildFile(file_proto); + ASSERT_TRUE(file != NULL); + + ASSERT_EQ(1, file->enum_type_count()); + const EnumDescriptor* enum_type = file->enum_type(0); + ASSERT_EQ(2, enum_type->value_count()); + const EnumValueDescriptor* enum_value_a = enum_type->value(0); + const EnumValueDescriptor* enum_value_b = enum_type->value(1); + + ASSERT_EQ(1, file->message_type_count()); + const Descriptor* message = file->message_type(0); + + ASSERT_EQ(21, message->field_count()); + + // Check the default values. + ASSERT_TRUE(message->field(0)->has_default_value()); + ASSERT_TRUE(message->field(1)->has_default_value()); + ASSERT_TRUE(message->field(2)->has_default_value()); + ASSERT_TRUE(message->field(3)->has_default_value()); + ASSERT_TRUE(message->field(4)->has_default_value()); + ASSERT_TRUE(message->field(5)->has_default_value()); + ASSERT_TRUE(message->field(6)->has_default_value()); + ASSERT_TRUE(message->field(7)->has_default_value()); + ASSERT_TRUE(message->field(8)->has_default_value()); + ASSERT_TRUE(message->field(9)->has_default_value()); + ASSERT_TRUE(message->field(10)->has_default_value()); + + EXPECT_EQ(-1 , message->field(0)->default_value_int32 ()); + EXPECT_EQ(-GOOGLE_ULONGLONG(1000000000000), + message->field(1)->default_value_int64 ()); + EXPECT_EQ(42 , message->field(2)->default_value_uint32()); + EXPECT_EQ(GOOGLE_ULONGLONG(2000000000000), + message->field(3)->default_value_uint64()); + EXPECT_EQ(4.5 , message->field(4)->default_value_float ()); + EXPECT_EQ(10e100 , message->field(5)->default_value_double()); + EXPECT_EQ(true , message->field(6)->default_value_bool ()); + EXPECT_EQ("hello" , message->field(7)->default_value_string()); + EXPECT_EQ("\001\002\003" , message->field(8)->default_value_string()); + EXPECT_EQ(enum_value_b , message->field(9)->default_value_enum ()); + EXPECT_EQ("" , message->field(10)->default_value_string()); + + ASSERT_FALSE(message->field(11)->has_default_value()); + ASSERT_FALSE(message->field(12)->has_default_value()); + ASSERT_FALSE(message->field(13)->has_default_value()); + ASSERT_FALSE(message->field(14)->has_default_value()); + ASSERT_FALSE(message->field(15)->has_default_value()); + ASSERT_FALSE(message->field(16)->has_default_value()); + ASSERT_FALSE(message->field(17)->has_default_value()); + ASSERT_FALSE(message->field(18)->has_default_value()); + ASSERT_FALSE(message->field(19)->has_default_value()); + ASSERT_FALSE(message->field(20)->has_default_value()); + + EXPECT_EQ(0 , message->field(11)->default_value_int32 ()); + EXPECT_EQ(0 , message->field(12)->default_value_int64 ()); + EXPECT_EQ(0 , message->field(13)->default_value_uint32()); + EXPECT_EQ(0 , message->field(14)->default_value_uint64()); + EXPECT_EQ(0.0f , message->field(15)->default_value_float ()); + EXPECT_EQ(0.0 , message->field(16)->default_value_double()); + EXPECT_EQ(false, message->field(17)->default_value_bool ()); + EXPECT_EQ("" , message->field(18)->default_value_string()); + EXPECT_EQ("" , message->field(19)->default_value_string()); + EXPECT_EQ(enum_value_a, message->field(20)->default_value_enum()); +} + +TEST_F(MiscTest, FieldOptions) { + // Try setting field options. + + FileDescriptorProto file_proto; + file_proto.set_name("foo.proto"); + + DescriptorProto* message_proto = AddMessage(&file_proto, "TestMessage"); + AddField(message_proto, "foo", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + FieldDescriptorProto* bar_proto = + AddField(message_proto, "bar", 2, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + FieldOptions* options = bar_proto->mutable_options(); + options->set_ctype(FieldOptions::CORD); + + // Build the descriptors and get the pointers. + DescriptorPool pool; + const FileDescriptor* file = pool.BuildFile(file_proto); + ASSERT_TRUE(file != NULL); + + ASSERT_EQ(1, file->message_type_count()); + const Descriptor* message = file->message_type(0); + + ASSERT_EQ(2, message->field_count()); + const FieldDescriptor* foo = message->field(0); + const FieldDescriptor* bar = message->field(1); + + // "foo" had no options set, so it should return the default options. + EXPECT_EQ(&FieldOptions::default_instance(), &foo->options()); + + // "bar" had options set. + EXPECT_NE(&FieldOptions::default_instance(), options); + EXPECT_TRUE(bar->options().has_ctype()); + EXPECT_EQ(FieldOptions::CORD, bar->options().ctype()); +} + +// =================================================================== + +// The tests below trigger every unique call to AddError() in descriptor.cc, +// in the order in which they appear in that file. I'm using TextFormat here +// to specify the input descriptors because building them using code would +// be too bulky. + +class MockErrorCollector : public DescriptorPool::ErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, + const string& element_name, const Message* descriptor, + ErrorLocation location, const string& message) { + const char* location_name = NULL; + switch (location) { + case NAME : location_name = "NAME" ; break; + case NUMBER : location_name = "NUMBER" ; break; + case TYPE : location_name = "TYPE" ; break; + case EXTENDEE : location_name = "EXTENDEE" ; break; + case DEFAULT_VALUE: location_name = "DEFAULT_VALUE"; break; + case INPUT_TYPE : location_name = "INPUT_TYPE" ; break; + case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break; + case OTHER : location_name = "OTHER" ; break; + } + + strings::SubstituteAndAppend( + &text_, "$0: $1: $2: $3\n", + filename, element_name, location_name, message); + } +}; + +class ValidationErrorTest : public testing::Test { + protected: + // Parse file_text as a FileDescriptorProto in text format and add it + // to the DescriptorPool. Expect no errors. + void BuildFile(const string& file_text) { + FileDescriptorProto file_proto; + ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + ASSERT_TRUE(pool_.BuildFile(file_proto) != NULL); + } + + // Parse file_text as a FileDescriptorProto in text format and add it + // to the DescriptorPool. Expect errors to be produced which match the + // given error text. + void BuildFileWithErrors(const string& file_text, + const string& expected_errors) { + FileDescriptorProto file_proto; + ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + + MockErrorCollector error_collector; + EXPECT_TRUE( + pool_.BuildFileCollectingErrors(file_proto, &error_collector) == NULL); + EXPECT_EQ(expected_errors, error_collector.text_); + } + + DescriptorPool pool_; +}; + +TEST_F(ValidationErrorTest, AlreadyDefined) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }" + "message_type { name: \"Foo\" }", + + "foo.proto: Foo: NAME: \"Foo\" is already defined.\n"); +} + +TEST_F(ValidationErrorTest, AlreadyDefinedInPackage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "package: \"foo.bar\" " + "message_type { name: \"Foo\" }" + "message_type { name: \"Foo\" }", + + "foo.proto: foo.bar.Foo: NAME: \"Foo\" is already defined in " + "\"foo.bar\".\n"); +} + +TEST_F(ValidationErrorTest, AlreadyDefinedInOtherFile) { + BuildFile( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }"); + + BuildFileWithErrors( + "name: \"bar.proto\" " + "message_type { name: \"Foo\" }", + + "bar.proto: Foo: NAME: \"Foo\" is already defined in file " + "\"foo.proto\".\n"); +} + +TEST_F(ValidationErrorTest, PackageAlreadyDefined) { + BuildFile( + "name: \"foo.proto\" " + "message_type { name: \"foo\" }"); + BuildFileWithErrors( + "name: \"bar.proto\" " + "package: \"foo.bar\"", + + "bar.proto: foo: NAME: \"foo\" is already defined (as something other " + "than a package) in file \"foo.proto\".\n"); +} + +TEST_F(ValidationErrorTest, MissingName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { }", + + "foo.proto: : NAME: Missing name.\n"); +} + +TEST_F(ValidationErrorTest, InvalidName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"$\" }", + + "foo.proto: $: NAME: \"$\" is not a valid identifier.\n"); +} + +TEST_F(ValidationErrorTest, InvalidPackageName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "package: \"foo.$\"", + + "foo.proto: foo.$: NAME: \"$\" is not a valid identifier.\n"); +} + +TEST_F(ValidationErrorTest, MissingFileName) { + BuildFileWithErrors( + "", + + ": : OTHER: Missing field: FileDescriptorProto.name.\n"); +} + +TEST_F(ValidationErrorTest, DupeDependency) { + BuildFile("name: \"foo.proto\""); + BuildFileWithErrors( + "name: \"bar.proto\" " + "dependency: \"foo.proto\" " + "dependency: \"foo.proto\" ", + + "bar.proto: bar.proto: OTHER: Import \"foo.proto\" was listed twice.\n"); +} + +TEST_F(ValidationErrorTest, UnknownDependency) { + BuildFileWithErrors( + "name: \"bar.proto\" " + "dependency: \"foo.proto\" ", + + "bar.proto: bar.proto: OTHER: Import \"foo.proto\" has not been loaded.\n"); +} + +TEST_F(ValidationErrorTest, DupeFile) { + BuildFile( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }"); + // Note: We should *not* get redundant errors about "Foo" already being + // defined. + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }", + + "foo.proto: foo.proto: OTHER: A file with this name is already in the " + "pool.\n"); +} + +TEST_F(ValidationErrorTest, FieldInExtensionRange) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 9 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"bar\" number: 10 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"baz\" number: 19 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"qux\" number: 20 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " extension_range { start: 10 end: 20 }" + "}", + + "foo.proto: Foo.bar: NUMBER: Extension range 10 to 19 includes field " + "\"bar\" (10).\n" + "foo.proto: Foo.baz: NUMBER: Extension range 10 to 19 includes field " + "\"baz\" (19).\n"); +} + +TEST_F(ValidationErrorTest, OverlappingExtensionRanges) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 20 }" + " extension_range { start: 20 end: 30 }" + " extension_range { start: 19 end: 21 }" + "}", + + "foo.proto: Foo: NUMBER: Extension range 19 to 20 overlaps with " + "already-defined range 10 to 19.\n" + "foo.proto: Foo: NUMBER: Extension range 19 to 20 overlaps with " + "already-defined range 20 to 29.\n"); +} + +TEST_F(ValidationErrorTest, InvalidDefaults) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + + // Invalid number. + " field { name: \"foo\" number: 1 label: LABEL_OPTIONAL type: TYPE_INT32" + " default_value: \"abc\" }" + + // Empty default value. + " field { name: \"bar\" number: 2 label: LABEL_OPTIONAL type: TYPE_INT32" + " default_value: \"\" }" + + // Invalid boolean. + " field { name: \"baz\" number: 3 label: LABEL_OPTIONAL type: TYPE_BOOL" + " default_value: \"abc\" }" + + // Messages can't have defaults. + " field { name: \"qux\" number: 4 label: LABEL_OPTIONAL type: TYPE_MESSAGE" + " default_value: \"abc\" type_name: \"Foo\" }" + + // Same thing, but we don't know that this field has message type until + // we look up the type name. + " field { name: \"quux\" number: 5 label: LABEL_OPTIONAL" + " default_value: \"abc\" type_name: \"Foo\" }" + "}", + + "foo.proto: Foo.foo: DEFAULT_VALUE: Couldn't parse default value.\n" + "foo.proto: Foo.bar: DEFAULT_VALUE: Couldn't parse default value.\n" + "foo.proto: Foo.baz: DEFAULT_VALUE: Boolean default must be true or " + "false.\n" + "foo.proto: Foo.qux: DEFAULT_VALUE: Messages can't have default values.\n" + "foo.proto: Foo.quux: DEFAULT_VALUE: Messages can't have default " + "values.\n"); +} + +TEST_F(ValidationErrorTest, NegativeFieldNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: -1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: NUMBER: Field numbers must be positive integers.\n"); +} + +TEST_F(ValidationErrorTest, HugeFieldNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 0x70000000 " + " label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: NUMBER: Field numbers cannot be greater than " + "536870911.\n"); +} + +TEST_F(ValidationErrorTest, ReservedFieldNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field {name:\"foo\" number: 18999 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field {name:\"bar\" number: 19000 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field {name:\"baz\" number: 19999 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field {name:\"qux\" number: 20000 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.bar: NUMBER: Field numbers 19000 through 19999 are " + "reserved for the protocol buffer library implementation.\n" + "foo.proto: Foo.baz: NUMBER: Field numbers 19000 through 19999 are " + "reserved for the protocol buffer library implementation.\n"); +} + +TEST_F(ValidationErrorTest, ExtensionMissingExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension { name: \"foo\" number: 1 label: LABEL_OPTIONAL" + " type_name: \"Foo\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: FieldDescriptorProto.extendee not set for " + "extension field.\n"); +} + +TEST_F(ValidationErrorTest, NonExtensionWithExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Bar\"" + " extension_range { start: 1 end: 2 }" + "}" + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 1 label: LABEL_OPTIONAL" + " type_name: \"Foo\" extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: FieldDescriptorProto.extendee set for " + "non-extension field.\n"); +} + +TEST_F(ValidationErrorTest, FieldNumberConflict) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"bar\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.bar: NUMBER: Field number 1 has already been used in " + "\"Foo\" by field \"foo\".\n"); +} + +TEST_F(ValidationErrorTest, BadMessageSetExtensionType) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"MessageSet\"" + " options { message_set_wire_format: true }" + " extension_range { start: 4 end: 5 }" + "}" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:4 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"MessageSet\" }" + "}", + + "foo.proto: Foo.foo: TYPE: Extensions of MessageSets must be optional " + "messages.\n"); +} + +TEST_F(ValidationErrorTest, BadMessageSetExtensionLabel) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"MessageSet\"" + " options { message_set_wire_format: true }" + " extension_range { start: 4 end: 5 }" + "}" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:4 label:LABEL_REPEATED type:TYPE_MESSAGE" + " type_name: \"Foo\" extendee: \"MessageSet\" }" + "}", + + "foo.proto: Foo.foo: TYPE: Extensions of MessageSets must be optional " + "messages.\n"); +} + +TEST_F(ValidationErrorTest, FieldInMessageSet) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " options { message_set_wire_format: true }" + " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: NAME: MessageSets cannot have fields, only " + "extensions.\n"); +} + +TEST_F(ValidationErrorTest, NegativeExtensionRangeNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: -10 end: -1 }" + "}", + + "foo.proto: Foo: NUMBER: Extension numbers must be positive integers.\n"); +} + +TEST_F(ValidationErrorTest, HugeExtensionRangeNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 1 end: 0x70000000 }" + "}", + + "foo.proto: Foo: NUMBER: Extension numbers cannot be greater than " + "536870911.\n"); +} + +TEST_F(ValidationErrorTest, ExtensionRangeEndBeforeStart) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 10 }" + " extension_range { start: 10 end: 5 }" + "}", + + "foo.proto: Foo: NUMBER: Extension range end number must be greater than " + "start number.\n" + "foo.proto: Foo: NUMBER: Extension range end number must be greater than " + "start number.\n"); +} + +TEST_F(ValidationErrorTest, EmptyEnum) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Foo\" }" + // Also use the empty enum in a message to make sure there are no crashes + // during validation (possible if the code attempts to derive a default + // value for the field). + "message_type {" + " name: \"Bar\"" + " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type_name:\"Foo\" }" + " field { name: \"bar\" number: 2 label:LABEL_OPTIONAL type_name:\"Foo\" " + " default_value: \"NO_SUCH_VALUE\" }" + "}", + + "foo.proto: Foo: NAME: Enums must contain at least one value.\n" + "foo.proto: Bar.bar: DEFAULT_VALUE: Enum type \"Foo\" has no value named " + "\"NO_SUCH_VALUE\".\n"); +} + +TEST_F(ValidationErrorTest, UndefinedExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, NonMessageExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } }" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, NotAnExtensionNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Bar\"" + "}" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: NUMBER: \"Bar\" does not declare 1 as an extension " + "number.\n"); +} + +TEST_F(ValidationErrorTest, UndefinedFieldType) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }" + "}", + + "foo.proto: Foo.foo: TYPE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, FieldTypeDefinedInUndeclaredDependency) { + BuildFile( + "name: \"bar.proto\" " + "message_type { name: \"Bar\" } "); + + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }" + "}", + "foo.proto: Foo.foo: TYPE: \"Bar\" seems to be defined in \"bar.proto\", " + "which is not imported by \"foo.proto\". To use it here, please add the " + "necessary import.\n"); +} + +TEST_F(ValidationErrorTest, SearchMostLocalFirst) { + // The following should produce an error that Bar.Baz is not defined: + // message Bar { message Baz {} } + // message Foo { + // message Bar { + // // Placing "message Baz{}" here, or removing Foo.Bar altogether, + // // would fix the error. + // } + // optional Bar.Baz baz = 1; + // } + // An one point the lookup code incorrectly did not produce an error in this + // case, because when looking for Bar.Baz, it would try "Foo.Bar.Baz" first, + // fail, and ten try "Bar.Baz" and succeed, even though "Bar" should actually + // refer to the inner Bar, not the outer one. + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Bar\"" + " nested_type { name: \"Baz\" }" + "}" + "message_type {" + " name: \"Foo\"" + " nested_type { name: \"Bar\" }" + " field { name:\"baz\" number:1 label:LABEL_OPTIONAL" + " type_name:\"Bar.Baz\" }" + "}", + + "foo.proto: Foo.baz: TYPE: \"Bar.Baz\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, PackageOriginallyDeclaredInTransitiveDependent) { + // Imagine we have the following: + // + // foo.proto: + // package foo.bar; + // bar.proto: + // package foo.bar; + // import "foo.proto"; + // message Bar {} + // baz.proto: + // package foo; + // import "bar.proto" + // message Baz { optional bar.Bar qux = 1; } + // + // When validating baz.proto, we will look up "bar.Bar". As part of this + // lookup, we first lookup "bar" then try to find "Bar" within it. "bar" + // should resolve to "foo.bar". Note, though, that "foo.bar" was originally + // defined in foo.proto, which is not a direct dependency of baz.proto. The + // implementation of FindSymbol() normally only returns symbols in direct + // dependencies, not indirect ones. This test insures that this does not + // prevent it from finding "foo.bar". + + BuildFile( + "name: \"foo.proto\" " + "package: \"foo.bar\" "); + BuildFile( + "name: \"bar.proto\" " + "package: \"foo.bar\" " + "dependency: \"foo.proto\" " + "message_type { name: \"Bar\" }"); + BuildFile( + "name: \"baz.proto\" " + "package: \"foo\" " + "dependency: \"bar.proto\" " + "message_type { " + " name: \"Baz\" " + " field { name:\"qux\" number:1 label:LABEL_OPTIONAL " + " type_name:\"bar.Bar\" }" + "}"); +} + +TEST_F(ValidationErrorTest, FieldTypeNotAType) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"bar\" }" + " field { name:\"bar\" number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: TYPE: \"bar\" is not a type.\n"); +} + +TEST_F(ValidationErrorTest, EnumFieldTypeIsMessage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Bar\" } " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM" + " type_name:\"Bar\" }" + "}", + + "foo.proto: Foo.foo: TYPE: \"Bar\" is not an enum type.\n"); +} + +TEST_F(ValidationErrorTest, MessageFieldTypeIsEnum) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE" + " type_name:\"Bar\" }" + "}", + + "foo.proto: Foo.foo: TYPE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, BadEnumDefaultValue) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\"" + " default_value:\"NO_SUCH_VALUE\" }" + "}", + + "foo.proto: Foo.foo: DEFAULT_VALUE: Enum type \"Bar\" has no value named " + "\"NO_SUCH_VALUE\".\n"); +} + +TEST_F(ValidationErrorTest, PrimitiveWithTypeName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " type_name:\"Foo\" }" + "}", + + "foo.proto: Foo.foo: TYPE: Field with primitive type has type_name.\n"); +} + +TEST_F(ValidationErrorTest, NonPrimitiveWithoutTypeName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE }" + "}", + + "foo.proto: Foo.foo: TYPE: Field with message or enum type missing " + "type_name.\n"); +} + +TEST_F(ValidationErrorTest, InputTypeNotDefined) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Bar\" output_type: \"Foo\" }" + "}", + + "foo.proto: TestService.A: INPUT_TYPE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, InputTypeNotAMessage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Bar\" output_type: \"Foo\" }" + "}", + + "foo.proto: TestService.A: INPUT_TYPE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, OutputTypeNotDefined) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Foo\" output_type: \"Bar\" }" + "}", + + "foo.proto: TestService.A: OUTPUT_TYPE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, OutputTypeNotAMessage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Foo\" output_type: \"Bar\" }" + "}", + + "foo.proto: TestService.A: OUTPUT_TYPE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, RollbackAfterError) { + // Build a file which contains every kind of construct but references an + // undefined type. All these constructs will be added to the symbol table + // before the undefined type error is noticed. The DescriptorPool will then + // have to roll everything back. + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" + "} " + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"BAR\" number:1 }" + "} " + "service {" + " name: \"TestService\"" + " method {" + " name: \"Baz\"" + " input_type: \"NoSuchType\"" // error + " output_type: \"TestMessage\"" + " }" + "}", + + "foo.proto: TestService.Baz: INPUT_TYPE: \"NoSuchType\" is not defined.\n"); + + // Make sure that if we build the same file again with the error fixed, + // it works. If the above rollback was incomplete, then some symbols will + // be left defined, and this second attempt will fail since it tries to + // re-define the same symbols. + BuildFile( + "name: \"foo.proto\" " + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" + "} " + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"BAR\" number:1 }" + "} " + "service {" + " name: \"TestService\"" + " method { name:\"Baz\"" + " input_type:\"TestMessage\"" + " output_type:\"TestMessage\" }" + "}"); +} + +TEST_F(ValidationErrorTest, ErrorsReportedToLogError) { + // Test that errors are reported to GOOGLE_LOG(ERROR) if no error collector is + // provided. + + FileDescriptorProto file_proto; + ASSERT_TRUE(TextFormat::ParseFromString( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "message_type { name: \"Foo\" } ", + &file_proto)); + + vector errors; + + { + ScopedMemoryLog log; + EXPECT_TRUE(pool_.BuildFile(file_proto) == NULL); + errors = log.GetMessages(ERROR); + } + + ASSERT_EQ(2, errors.size()); + + EXPECT_EQ("Invalid proto descriptor for file \"foo.proto\":", errors[0]); + EXPECT_EQ(" Foo: \"Foo\" is already defined.", errors[1]); +} + +// =================================================================== +// DescriptorDatabase + +static void AddToDatabase(SimpleDescriptorDatabase* database, + const char* file_text) { + FileDescriptorProto file_proto; + EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + database->Add(file_proto); +} + +class DatabaseBackedPoolTest : public testing::Test { + protected: + DatabaseBackedPoolTest() {} + + SimpleDescriptorDatabase database_; + + virtual void SetUp() { + AddToDatabase(&database_, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" extension_range { start: 1 end: 100 } } " + "enum_type { name:\"TestEnum\" value { name:\"DUMMY\" number:0 } } " + "service { name:\"TestService\" } "); + AddToDatabase(&database_, + "name: \"bar.proto\" " + "dependency: \"foo.proto\" " + "message_type { name:\"Bar\" } " + "extension { name:\"foo_ext\" extendee: \".Foo\" number:5 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + } + + // We can't inject a file containing errors into a DescriptorPool, so we + // need an actual mock DescriptorDatabase to test errors. + class ErrorDescriptorDatabase : public DescriptorDatabase { + public: + ErrorDescriptorDatabase() {} + ~ErrorDescriptorDatabase() {} + + // implements DescriptorDatabase --------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output) { + // error.proto and error2.proto cyclically import each other. + if (filename == "error.proto") { + output->Clear(); + output->set_name("error.proto"); + output->add_dependency("error2.proto"); + return true; + } else if (filename == "error2.proto") { + output->Clear(); + output->set_name("error2.proto"); + output->add_dependency("error.proto"); + return true; + } else { + return false; + } + } + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) { + return false; + } + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) { + return false; + } + }; + + // A DescriptorDatabase that counts how many times each method has been + // called and forwards to some other DescriptorDatabase. + class CallCountingDatabase : public DescriptorDatabase { + public: + CallCountingDatabase(DescriptorDatabase* wrapped_db) + : wrapped_db_(wrapped_db) { + Clear(); + } + ~CallCountingDatabase() {} + + DescriptorDatabase* wrapped_db_; + + int call_count_; + + void Clear() { + call_count_ = 0; + } + + // implements DescriptorDatabase --------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output) { + ++call_count_; + return wrapped_db_->FindFileByName(filename, output); + } + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) { + ++call_count_; + return wrapped_db_->FindFileContainingSymbol(symbol_name, output); + } + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) { + ++call_count_; + return wrapped_db_->FindFileContainingExtension( + containing_type, field_number, output); + } + }; + + // A DescriptorDatabase which falsely always returns foo.proto when searching + // for any symbol or extension number. This shouldn't cause the + // DescriptorPool to reload foo.proto if it is already loaded. + class FalsePositiveDatabase : public DescriptorDatabase { + public: + FalsePositiveDatabase(DescriptorDatabase* wrapped_db) + : wrapped_db_(wrapped_db) {} + ~FalsePositiveDatabase() {} + + DescriptorDatabase* wrapped_db_; + + // implements DescriptorDatabase --------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output) { + return wrapped_db_->FindFileByName(filename, output); + } + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) { + return FindFileByName("foo.proto", output); + } + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) { + return FindFileByName("foo.proto", output); + } + }; +}; + +TEST_F(DatabaseBackedPoolTest, FindFileByName) { + DescriptorPool pool(&database_); + + const FileDescriptor* foo = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(foo != NULL); + EXPECT_EQ("foo.proto", foo->name()); + ASSERT_EQ(1, foo->message_type_count()); + EXPECT_EQ("Foo", foo->message_type(0)->name()); + + EXPECT_EQ(foo, pool.FindFileByName("foo.proto")); + + EXPECT_TRUE(pool.FindFileByName("no_such_file.proto") == NULL); +} + +TEST_F(DatabaseBackedPoolTest, FindDependencyBeforeDependent) { + DescriptorPool pool(&database_); + + const FileDescriptor* foo = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(foo != NULL); + EXPECT_EQ("foo.proto", foo->name()); + ASSERT_EQ(1, foo->message_type_count()); + EXPECT_EQ("Foo", foo->message_type(0)->name()); + + const FileDescriptor* bar = pool.FindFileByName("bar.proto"); + ASSERT_TRUE(bar != NULL); + EXPECT_EQ("bar.proto", bar->name()); + ASSERT_EQ(1, bar->message_type_count()); + EXPECT_EQ("Bar", bar->message_type(0)->name()); + + ASSERT_EQ(1, bar->dependency_count()); + EXPECT_EQ(foo, bar->dependency(0)); +} + +TEST_F(DatabaseBackedPoolTest, FindDependentBeforeDependency) { + DescriptorPool pool(&database_); + + const FileDescriptor* bar = pool.FindFileByName("bar.proto"); + ASSERT_TRUE(bar != NULL); + EXPECT_EQ("bar.proto", bar->name()); + ASSERT_EQ(1, bar->message_type_count()); + ASSERT_EQ("Bar", bar->message_type(0)->name()); + + const FileDescriptor* foo = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(foo != NULL); + EXPECT_EQ("foo.proto", foo->name()); + ASSERT_EQ(1, foo->message_type_count()); + ASSERT_EQ("Foo", foo->message_type(0)->name()); + + ASSERT_EQ(1, bar->dependency_count()); + EXPECT_EQ(foo, bar->dependency(0)); +} + +TEST_F(DatabaseBackedPoolTest, FindFileContainingSymbol) { + DescriptorPool pool(&database_); + + const FileDescriptor* file = pool.FindFileContainingSymbol("Foo"); + ASSERT_TRUE(file != NULL); + EXPECT_EQ("foo.proto", file->name()); + EXPECT_EQ(file, pool.FindFileByName("foo.proto")); + + EXPECT_TRUE(pool.FindFileContainingSymbol("NoSuchSymbol") == NULL); +} + +TEST_F(DatabaseBackedPoolTest, FindMessageTypeByName) { + DescriptorPool pool(&database_); + + const Descriptor* type = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(type != NULL); + EXPECT_EQ("Foo", type->name()); + EXPECT_EQ(type->file(), pool.FindFileByName("foo.proto")); + + EXPECT_TRUE(pool.FindMessageTypeByName("NoSuchType") == NULL); +} + +TEST_F(DatabaseBackedPoolTest, FindExtensionByNumber) { + DescriptorPool pool(&database_); + + const Descriptor* foo = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(foo != NULL); + + const FieldDescriptor* extension = pool.FindExtensionByNumber(foo, 5); + ASSERT_TRUE(extension != NULL); + EXPECT_EQ("foo_ext", extension->name()); + EXPECT_EQ(extension->file(), pool.FindFileByName("bar.proto")); + + EXPECT_TRUE(pool.FindExtensionByNumber(foo, 12) == NULL); +} + +TEST_F(DatabaseBackedPoolTest, ErrorWithoutErrorCollector) { + ErrorDescriptorDatabase error_database; + DescriptorPool pool(&error_database); + + vector errors; + + { + ScopedMemoryLog log; + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + errors = log.GetMessages(ERROR); + } + + EXPECT_FALSE(errors.empty()); +} + +TEST_F(DatabaseBackedPoolTest, ErrorWithErrorCollector) { + ErrorDescriptorDatabase error_database; + MockErrorCollector error_collector; + DescriptorPool pool(&error_database, &error_collector); + + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + EXPECT_EQ( + "error.proto: error.proto: OTHER: File recursively imports itself: " + "error.proto -> error2.proto -> error.proto\n" + "error2.proto: error2.proto: OTHER: Import \"error.proto\" was not " + "found or had errors.\n" + "error.proto: error.proto: OTHER: Import \"error2.proto\" was not " + "found or had errors.\n", + error_collector.text_); +} + +TEST_F(DatabaseBackedPoolTest, UnittestProto) { + // Try to load all of unittest.proto from a DescriptorDatabase. This should + // thoroughly test all paths through DescriptorBuilder to insure that there + // are no deadlocking problems when pool_->mutex_ is non-NULL. + const FileDescriptor* original_file = + protobuf_unittest::TestAllTypes::descriptor()->file(); + + DescriptorPoolDatabase database(*DescriptorPool::generated_pool()); + DescriptorPool pool(&database); + const FileDescriptor* file_from_database = + pool.FindFileByName(original_file->name()); + + ASSERT_TRUE(file_from_database != NULL); + + FileDescriptorProto original_file_proto; + original_file->CopyTo(&original_file_proto); + + FileDescriptorProto file_from_database_proto; + file_from_database->CopyTo(&file_from_database_proto); + + EXPECT_EQ(original_file_proto.DebugString(), + file_from_database_proto.DebugString()); +} + +TEST_F(DatabaseBackedPoolTest, DoesntRetryDbUnnecessarily) { + // Searching for a child of an existing descriptor should never fall back + // to the DescriptorDatabase even if it isn't found, because we know all + // children are already loaded. + CallCountingDatabase call_counter(&database_); + DescriptorPool pool(&call_counter); + + const FileDescriptor* file = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(file != NULL); + const Descriptor* foo = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(foo != NULL); + const EnumDescriptor* test_enum = pool.FindEnumTypeByName("TestEnum"); + ASSERT_TRUE(test_enum != NULL); + const ServiceDescriptor* test_service = pool.FindServiceByName("TestService"); + ASSERT_TRUE(test_service != NULL); + + EXPECT_NE(0, call_counter.call_count_); + call_counter.Clear(); + + EXPECT_TRUE(foo->FindFieldByName("no_such_field") == NULL); + EXPECT_TRUE(foo->FindExtensionByName("no_such_extension") == NULL); + EXPECT_TRUE(foo->FindNestedTypeByName("NoSuchMessageType") == NULL); + EXPECT_TRUE(foo->FindEnumTypeByName("NoSuchEnumType") == NULL); + EXPECT_TRUE(foo->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(test_enum->FindValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(test_service->FindMethodByName("NoSuchMethod") == NULL); + + EXPECT_TRUE(file->FindMessageTypeByName("NoSuchMessageType") == NULL); + EXPECT_TRUE(file->FindEnumTypeByName("NoSuchEnumType") == NULL); + EXPECT_TRUE(file->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(file->FindServiceByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(file->FindExtensionByName("no_such_extension") == NULL); + EXPECT_EQ(0, call_counter.call_count_); +} + +TEST_F(DatabaseBackedPoolTest, DoesntReloadFilesUncesessarily) { + // If FindFileContainingSymbol() or FindFileContainingExtension() return a + // file that is already in the DescriptorPool, it should not attempt to + // reload the file. + FalsePositiveDatabase false_positive_database(&database_); + MockErrorCollector error_collector; + DescriptorPool pool(&false_positive_database, &error_collector); + + // First make sure foo.proto is loaded. + const Descriptor* foo = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(foo != NULL); + + // Try inducing false positives. + EXPECT_TRUE(pool.FindMessageTypeByName("NoSuchSymbol") == NULL); + EXPECT_TRUE(pool.FindExtensionByNumber(foo, 22) == NULL); + + // No errors should have been reported. (If foo.proto was incorrectly + // loaded multiple times, errors would have been reported.) + EXPECT_EQ("", error_collector.text_); +} + +TEST_F(DatabaseBackedPoolTest, DoesntReloadKnownBadFiles) { + ErrorDescriptorDatabase error_database; + MockErrorCollector error_collector; + DescriptorPool pool(&error_database, &error_collector); + + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + error_collector.text_.clear(); + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + EXPECT_EQ("", error_collector.text_); +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc new file mode 100644 index 00000000..43e2451e --- /dev/null +++ b/src/google/protobuf/dynamic_message.cc @@ -0,0 +1,475 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// DynamicMessage is implemented by constructing a data structure which +// has roughly the same memory layout as a generated message would have. +// Then, we use GeneratedMessageReflection to implement our reflection +// interface. All the other operations we need to implement (e.g. +// parsing, copying, etc.) are already implemented in terms of +// Message::Reflection, so the rest is easy. +// +// The up side of this strategy is that it's very efficient. We don't +// need to use hash_maps or generic representations of fields. The +// down side is that this is a low-level memory management hack which +// can be tricky to get right. +// +// As mentioned in the header, we only expose a DynamicMessageFactory +// publicly, not the DynamicMessage class itself. This is because +// GenericMessageReflection wants to have a pointer to a "default" +// copy of the class, with all fields initialized to their default +// values. We only want to construct one of these per message type, +// so DynamicMessageFactory stores a cache of default messages for +// each type it sees (each unique Descriptor pointer). The code +// refers to the "default" copy of the class as the "prototype". +// +// Note on memory allocation: This module often calls "operator new()" +// to allocate untyped memory, rather than calling something like +// "new uint8[]". This is because "operator new()" means "Give me some +// space which I can use as I please." while "new uint8[]" means "Give +// me an array of 8-bit integers.". In practice, the later may return +// a pointer that is not aligned correctly for general use. I believe +// Item 8 of "More Effective C++" discusses this in more detail, though +// I don't have the book on me right now so I'm not sure. + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +using internal::WireFormat; +using internal::ExtensionSet; +using internal::GeneratedMessageReflection; +using internal::GenericRepeatedField; + + +// =================================================================== +// Some helper tables and functions... + +namespace { + +// Compute the byte size of the in-memory representation of the field. +int FieldSpaceUsed(const FieldDescriptor* field) { + typedef FieldDescriptor FD; // avoid line wrapping + if (field->label() == FD::LABEL_REPEATED) { + switch (field->cpp_type()) { + case FD::CPPTYPE_INT32 : return sizeof(RepeatedField); + case FD::CPPTYPE_INT64 : return sizeof(RepeatedField); + case FD::CPPTYPE_UINT32 : return sizeof(RepeatedField); + case FD::CPPTYPE_UINT64 : return sizeof(RepeatedField); + case FD::CPPTYPE_DOUBLE : return sizeof(RepeatedField); + case FD::CPPTYPE_FLOAT : return sizeof(RepeatedField); + case FD::CPPTYPE_BOOL : return sizeof(RepeatedField); + case FD::CPPTYPE_ENUM : return sizeof(RepeatedField); + case FD::CPPTYPE_MESSAGE: return sizeof(RepeatedPtrField); + + case FD::CPPTYPE_STRING: + return sizeof(RepeatedPtrField); + break; + } + } else { + switch (field->cpp_type()) { + case FD::CPPTYPE_INT32 : return sizeof(int32 ); + case FD::CPPTYPE_INT64 : return sizeof(int64 ); + case FD::CPPTYPE_UINT32 : return sizeof(uint32 ); + case FD::CPPTYPE_UINT64 : return sizeof(uint64 ); + case FD::CPPTYPE_DOUBLE : return sizeof(double ); + case FD::CPPTYPE_FLOAT : return sizeof(float ); + case FD::CPPTYPE_BOOL : return sizeof(bool ); + case FD::CPPTYPE_ENUM : return sizeof(int ); + case FD::CPPTYPE_MESSAGE: return sizeof(Message*); + + case FD::CPPTYPE_STRING: + return sizeof(string*); + break; + } + } + + GOOGLE_LOG(DFATAL) << "Can't get here."; + return 0; +} + +struct DescendingFieldSizeOrder { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) { + // All repeated fields come first. + if (a->is_repeated()) { + if (b->is_repeated()) { + // Repeated fields and are not ordered with respect to each other. + return false; + } else { + return true; + } + } else if (b->is_repeated()) { + return false; + } else { + // Remaining fields in descending order by size. + return FieldSpaceUsed(a) > FieldSpaceUsed(b); + } + } +}; + +inline int DivideRoundingUp(int i, int j) { + return (i + (j - 1)) / j; +} + +#define bitsizeof(T) (sizeof(T) * 8) + +} // namespace + +// =================================================================== + +class DynamicMessage : public Message { + public: + DynamicMessage(const Descriptor* descriptor, + uint8* base, const uint8* prototype_base, + int size, const int offsets[], + const DescriptorPool* pool, DynamicMessageFactory* factory); + ~DynamicMessage(); + + // Called on the prototype after construction to initialize message fields. + void CrossLinkPrototypes(DynamicMessageFactory* factory); + + // implements Message ---------------------------------------------- + + Message* New() const; + + int GetCachedSize() const; + void SetCachedSize(int size) const; + + const Descriptor* GetDescriptor() const; + const Reflection* GetReflection() const; + Reflection* GetReflection(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessage); + + inline bool is_prototype() { return base_ == prototype_base_; } + + const Descriptor* descriptor_; + const DescriptorPool* descriptor_pool_; + DynamicMessageFactory* factory_; + scoped_ptr extensions_; + GeneratedMessageReflection reflection_; + uint8* base_; + const uint8* prototype_base_; + const int* offsets_; + int size_; + + // TODO(kenton): Make this an atomic when C++ supports it. + mutable int cached_byte_size_; +}; + +DynamicMessage::DynamicMessage(const Descriptor* descriptor, + uint8* base, const uint8* prototype_base, + int size, const int offsets[], + const DescriptorPool* pool, + DynamicMessageFactory* factory) + : descriptor_(descriptor), + descriptor_pool_((pool == NULL) ? descriptor->file()->pool() : pool), + factory_(factory), + extensions_(descriptor->extension_range_count() > 0 ? + new ExtensionSet(descriptor, descriptor_pool_, factory_) : + NULL), + reflection_(descriptor, base, prototype_base, offsets, + // has_bits + reinterpret_cast(base + size) - + DivideRoundingUp(descriptor->field_count(), bitsizeof(uint32)), + extensions_.get()), + base_(base), + prototype_base_(prototype_base), + offsets_(offsets), + size_(size), + cached_byte_size_(0) { + // We need to call constructors for various fields manually and set + // default values where appropriate. We use placement new to call + // constructors. If you haven't heard of placement new, I suggest Googling + // it now. We use placement new even for primitive types that don't have + // constructors for consistency. (In theory, placement new should be used + // any time you are trying to convert untyped memory to typed memory, though + // in practice that's not strictly necessary for types that don't have a + // constructor.) + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + void* field_ptr = base + offsets[i]; + switch (field->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, TYPE) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + if (!field->is_repeated()) { \ + new(field_ptr) TYPE(field->default_value_##TYPE()); \ + } else { \ + new(field_ptr) RepeatedField(); \ + } \ + break; + + HANDLE_TYPE(INT32 , int32 ); + HANDLE_TYPE(INT64 , int64 ); + HANDLE_TYPE(UINT32, uint32); + HANDLE_TYPE(UINT64, uint64); + HANDLE_TYPE(DOUBLE, double); + HANDLE_TYPE(FLOAT , float ); + HANDLE_TYPE(BOOL , bool ); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_ENUM: + if (!field->is_repeated()) { + new(field_ptr) int(field->default_value_enum()->number()); + } else { + new(field_ptr) RepeatedField(); + } + break; + + case FieldDescriptor::CPPTYPE_STRING: + if (!field->is_repeated()) { + if (is_prototype()) { + new(field_ptr) const string*(&field->default_value_string()); + } else { + string* default_value = + *reinterpret_cast( + prototype_base + offsets[i]); + new(field_ptr) string*(default_value); + } + } else { + new(field_ptr) RepeatedPtrField(); + } + break; + + case FieldDescriptor::CPPTYPE_MESSAGE: { + // If this object is the prototype, its CPPTYPE_MESSAGE fields + // must be initialized later, in CrossLinkPrototypes(), so we don't + // initialize them here. + if (!is_prototype()) { + if (!field->is_repeated()) { + new(field_ptr) Message*(NULL); + } else { + const RepeatedPtrField* prototype_field = + reinterpret_cast*>( + prototype_base + offsets[i]); + new(field_ptr) RepeatedPtrField( + prototype_field->prototype()); + } + } + break; + } + } + } +} + +DynamicMessage::~DynamicMessage() { + // We need to manually run the destructors for repeated fields and strings, + // just as we ran their constructors in the the DynamicMessage constructor. + // Additionally, if any singular embedded messages have been allocated, we + // need to delete them, UNLESS we are the prototype message of this type, + // in which case any embedded messages are other prototypes and shouldn't + // be touched. + const Descriptor* descriptor = GetDescriptor(); + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + void* field_ptr = base_ + offsets_[i]; + + if (field->is_repeated()) { + GenericRepeatedField* field = + reinterpret_cast(field_ptr); + field->~GenericRepeatedField(); + + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + string* ptr = *reinterpret_cast(field_ptr); + if (ptr != &field->default_value_string()) { + delete ptr; + } + } else if ((field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) && + !is_prototype()) { + Message* message = *reinterpret_cast(field_ptr); + if (message != NULL) { + delete message; + } + } + } + + // OK, now we can delete our base pointer. + operator delete(base_); + + // When the prototype is deleted, we also want to free the offsets table. + // (The prototype is only deleted when the factory that created it is + // deleted.) + if (is_prototype()) { + delete [] offsets_; + } +} + +void DynamicMessage::CrossLinkPrototypes(DynamicMessageFactory* factory) { + // This should only be called on the prototype message. + GOOGLE_CHECK(is_prototype()); + + // Cross-link default messages. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + void* field_ptr = base_ + offsets_[i]; + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + // For fields with message types, we need to cross-link with the + // prototype for the field's type. + const Message* field_prototype = + factory->GetPrototype(field->message_type()); + + if (field->is_repeated()) { + // For repeated fields, we actually construct the RepeatedPtrField + // here, but only for fields with message types. All other repeated + // fields are constructed in DynamicMessage's constructor. + new(field_ptr) RepeatedPtrField(field_prototype); + } else { + // For singular fields, the field is just a pointer which should + // point to the prototype. (OK to const_cast here because the + // prototype itself will only be available const to the outside + // world.) + new(field_ptr) Message*(const_cast(field_prototype)); + } + } + } +} + +Message* DynamicMessage::New() const { + uint8* new_base = reinterpret_cast(operator new(size_)); + memset(new_base, 0, size_); + + return new DynamicMessage(GetDescriptor(), new_base, prototype_base_, + size_, offsets_, descriptor_pool_, factory_); +} + +int DynamicMessage::GetCachedSize() const { + return cached_byte_size_; +} + +void DynamicMessage::SetCachedSize(int size) const { + // This is theoretically not thread-compatible, but in practice it works + // because if multiple threads write this simultaneously, they will be + // writing the exact same value. + cached_byte_size_ = size; +} + +const Descriptor* DynamicMessage::GetDescriptor() const { + return descriptor_; +} + +const Message::Reflection* DynamicMessage::GetReflection() const { + return &reflection_; +} + +Message::Reflection* DynamicMessage::GetReflection() { + return &reflection_; +} + +// =================================================================== + +struct DynamicMessageFactory::PrototypeMap { + typedef hash_map Map; + Map map_; +}; + +DynamicMessageFactory::DynamicMessageFactory() + : pool_(NULL), prototypes_(new PrototypeMap) { +} + +DynamicMessageFactory::DynamicMessageFactory(const DescriptorPool* pool) + : pool_(pool), prototypes_(new PrototypeMap) { +} + +DynamicMessageFactory::~DynamicMessageFactory() { + for (PrototypeMap::Map::iterator iter = prototypes_->map_.begin(); + iter != prototypes_->map_.end(); ++iter) { + delete iter->second; + } +} + + +const Message* DynamicMessageFactory::GetPrototype(const Descriptor* type) { + const Message** target = &prototypes_->map_[type]; + if (*target != NULL) { + // Already exists. + return *target; + } + + // We need to construct all the structures passed to + // GeneratedMessageReflection's constructor. This includes: + // - A block of memory that contains space for all the message's fields. + // - An array of integers indicating the byte offset of each field within + // this block. + // - A big bitfield containing a bit for each field indicating whether + // or not that field is set. + + // Compute size and offsets. + int* offsets = new int[type->field_count()]; + + // Sort the fields of this message in descending order by size. We + // assume that if we then pack the fields tightly in this order, all fields + // will end up properly-aligned, since all field sizes are powers of two or + // are multiples of the system word size. + scoped_array ordered_fields( + new const FieldDescriptor*[type->field_count()]); + for (int i = 0; i < type->field_count(); i++) { + ordered_fields[i] = type->field(i); + } + stable_sort(&ordered_fields[0], &ordered_fields[type->field_count()], + DescendingFieldSizeOrder()); + + // Decide all field offsets by packing in order. + int current_offset = 0; + + for (int i = 0; i < type->field_count(); i++) { + offsets[ordered_fields[i]->index()] = current_offset; + current_offset += FieldSpaceUsed(ordered_fields[i]); + } + + // Allocate space for all fields plus has_bits. We'll stick has_bits on + // the end. + int size = current_offset + + DivideRoundingUp(type->field_count(), bitsizeof(uint32)) * sizeof(uint32); + + // Round size up to the nearest 64-bit boundary just to make sure no + // clever allocators think that alignment is not necessary. This also + // insures that has_bits is properly-aligned, since we'll always align + // has_bits with the end of the structure. + size = DivideRoundingUp(size, sizeof(uint64)) * sizeof(uint64); + uint8* base = reinterpret_cast(operator new(size)); + memset(base, 0, size); + + // Construct message. + DynamicMessage* result = + new DynamicMessage(type, base, base, size, offsets, pool_, this); + *target = result; + result->CrossLinkPrototypes(this); + + return result; +} + +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/dynamic_message.h b/src/google/protobuf/dynamic_message.h new file mode 100644 index 00000000..e5a9f908 --- /dev/null +++ b/src/google/protobuf/dynamic_message.h @@ -0,0 +1,105 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Defines an implementation of Message which can emulate types which are not +// known at compile-time. + +#ifndef GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__ +#define GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__ + +#include +#include + +namespace google { +namespace protobuf { + +// Defined in other files. +class Descriptor; // descriptor.h +class DescriptorPool; // descriptor.h + +// Constructs implementations of Message which can emulate types which are not +// known at compile-time. +// +// Sometimes you want to be able to manipulate protocol types that you don't +// know about at compile time. It would be nice to be able to construct +// a Message object which implements the message type given by any arbitrary +// Descriptor. DynamicMessage provides this. +// +// As it turns out, a DynamicMessage needs to construct extra +// information about its type in order to operate. Most of this information +// can be shared between all DynamicMessages of the same type. But, caching +// this information in some sort of global map would be a bad idea, since +// the cached information for a particular descriptor could outlive the +// descriptor itself. To avoid this problem, DynamicMessageFactory +// encapsulates this "cache". All DynamicMessages of the same type created +// from the same factory will share the same support data. Any Descriptors +// used with a particular factory must outlive the factory. +class LIBPROTOBUF_EXPORT DynamicMessageFactory : public MessageFactory { + public: + // Construct a DynamicMessageFactory that will search for extensions in + // the DescriptorPool in which the exendee is defined. + DynamicMessageFactory(); + + // Construct a DynamicMessageFactory that will search for extensions in + // the given DescriptorPool. + DynamicMessageFactory(const DescriptorPool* pool); + ~DynamicMessageFactory(); + + // implements MessageFactory --------------------------------------- + + // Given a Descriptor, constructs the default (prototype) Message of that + // type. You can then call that message's New() method to construct a + // mutable message of that type. + // + // Calling this method twice with the same Descriptor returns the same + // object. The returned object remains property of the factory and will + // be destroyed when the factory is destroyed. Also, any objects created + // by calling the prototype's New() method share some data with the + // prototype, so these must be destoyed before the DynamicMessageFactory + // is destroyed. + // + // The given descriptor must outlive the returned message, and hence must + // outlive the DynamicMessageFactory. + // + // Note that while GetPrototype() is idempotent, it is not const. This + // implies that it is not thread-safe to call GetPrototype() on the same + // DynamicMessageFactory in two different threads simultaneously. However, + // the returned objects are just as thread-safe as any other Message. + const Message* GetPrototype(const Descriptor* type); + + private: + const DescriptorPool* pool_; + + // This struct just contains a hash_map. We can't #include from + // this header due to hacks needed for hash_map portability in the open source + // release. Namely, stubs/hash.h, which defines hash_map portably, is not a + // public header (for good reason), but dynamic_message.h is, and public + // headers may only #include other public headers. + struct PrototypeMap; + scoped_ptr prototypes_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessageFactory); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__ + diff --git a/src/google/protobuf/dynamic_message_unittest.cc b/src/google/protobuf/dynamic_message_unittest.cc new file mode 100644 index 00000000..5afac1fc --- /dev/null +++ b/src/google/protobuf/dynamic_message_unittest.cc @@ -0,0 +1,117 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Since the reflection interface for DynamicMessage is implemented by +// GenericMessageReflection, the only thing we really have to test is +// that DynamicMessage correctly sets up the information that +// GenericMessageReflection needs to use. So, we focus on that in this +// test. Other tests, such as generic_message_reflection_unittest and +// reflection_ops_unittest, cover the rest of the functionality used by +// DynamicMessage. + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { + +class DynamicMessageTest : public testing::Test { + protected: + DescriptorPool pool_; + DynamicMessageFactory factory_; + const Descriptor* descriptor_; + const Message* prototype_; + const Descriptor* extensions_descriptor_; + const Message* extensions_prototype_; + + DynamicMessageTest(): factory_(&pool_) {} + + virtual void SetUp() { + // We want to make sure that DynamicMessage works (particularly with + // extensions) even if we use descriptors that are *not* from compiled-in + // types, so we make copies of the descriptors for unittest.proto and + // unittest_import.proto. + FileDescriptorProto unittest_file; + FileDescriptorProto unittest_import_file; + + unittest::TestAllTypes::descriptor()->file()->CopyTo(&unittest_file); + unittest_import::ImportMessage::descriptor()->file()->CopyTo( + &unittest_import_file); + + ASSERT_TRUE(pool_.BuildFile(unittest_import_file) != NULL); + ASSERT_TRUE(pool_.BuildFile(unittest_file) != NULL); + + descriptor_ = pool_.FindMessageTypeByName("protobuf_unittest.TestAllTypes"); + ASSERT_TRUE(descriptor_ != NULL); + prototype_ = factory_.GetPrototype(descriptor_); + + extensions_descriptor_ = + pool_.FindMessageTypeByName("protobuf_unittest.TestAllExtensions"); + ASSERT_TRUE(extensions_descriptor_ != NULL); + extensions_prototype_ = factory_.GetPrototype(extensions_descriptor_); + } +}; + +TEST_F(DynamicMessageTest, Descriptor) { + // Check that the descriptor on the DynamicMessage matches the descriptor + // passed to GetPrototype(). + EXPECT_EQ(prototype_->GetDescriptor(), descriptor_); +} + +TEST_F(DynamicMessageTest, OnePrototype) { + // Check that requesting the same prototype twice produces the same object. + EXPECT_EQ(prototype_, factory_.GetPrototype(descriptor_)); +} + +TEST_F(DynamicMessageTest, Defaults) { + // Check that all default values are set correctly in the initial message. + TestUtil::ReflectionTester reflection_tester(descriptor_); + reflection_tester.ExpectClearViaReflection(*prototype_->GetReflection()); +} + +TEST_F(DynamicMessageTest, IndependentOffsets) { + // Check that all fields have independent offsets by setting each + // one to a unique value then checking that they all still have those + // unique values (i.e. they don't stomp each other). + scoped_ptr message(prototype_->New()); + TestUtil::ReflectionTester reflection_tester(descriptor_); + + reflection_tester.SetAllFieldsViaReflection(message->GetReflection()); + reflection_tester.ExpectAllFieldsSetViaReflection(*message->GetReflection()); +} + +TEST_F(DynamicMessageTest, Extensions) { + // Check that extensions work. + scoped_ptr message(extensions_prototype_->New()); + TestUtil::ReflectionTester reflection_tester(extensions_descriptor_); + + reflection_tester.SetAllFieldsViaReflection(message->GetReflection()); + reflection_tester.ExpectAllFieldsSetViaReflection(*message->GetReflection()); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc new file mode 100644 index 00000000..154f06f8 --- /dev/null +++ b/src/google/protobuf/extension_set.cc @@ -0,0 +1,735 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +// ------------------------------------------------------------------- +// Lookup functions + +const FieldDescriptor* +ExtensionSet::FindKnownExtensionByName(const string& name) const { + const FieldDescriptor* result = descriptor_pool_->FindExtensionByName(name); + if (result != NULL && result->containing_type() == extendee_) { + return result; + } + + if (extendee_->options().message_set_wire_format()) { + // MessageSet extensions may be identified by type name. + const Descriptor* type = descriptor_pool_->FindMessageTypeByName(name); + if (type != NULL) { + // Look for a matching extension in the foreign type's scope. + for (int i = 0; i < type->extension_count(); i++) { + const FieldDescriptor* extension = type->extension(i); + if (extension->containing_type() == extendee_ && + extension->type() == FieldDescriptor::TYPE_MESSAGE && + extension->is_optional() && + extension->message_type() == type) { + // Found it. + return extension; + } + } + } + } + + return NULL; +} + +const FieldDescriptor* +ExtensionSet::FindKnownExtensionByNumber(int number) const { + return descriptor_pool_->FindExtensionByNumber(extendee_, number); +} + +const FieldDescriptor* +ExtensionSet::FindKnownExtensionOrDie(int number) const { + const FieldDescriptor* descriptor = FindKnownExtensionByNumber(number); + if (descriptor == NULL) { + // This extension doesn't exist, so we have to crash. However, let's + // try to provide an informative error message. + if (descriptor_pool_ == DescriptorPool::generated_pool() && + message_factory_ == MessageFactory::generated_factory()) { + // This is probably the ExtensionSet for a generated class. + GOOGLE_LOG(FATAL) << ": No extension is registered for \"" + << extendee_->full_name() << "\" with number " + << number << ". Perhaps you were trying to access it via " + "the Reflection interface, but you provided a " + "FieldDescriptor which did not come from a linked-in " + "message type? This is not permitted; linkin-in message " + "types cannot use non-linked-in extensions. Try " + "converting to a DynamicMessage first."; + } else { + // This is probably a DynamicMessage. + GOOGLE_LOG(FATAL) << ": No extension is registered for \"" + << extendee_->full_name() << "\" with number " + << number << ". If you were using a DynamicMessage, " + "remember that you are only allowed to access extensions " + "which are defined in the DescriptorPool which you passed " + "to DynamicMessageFactory's constructor."; + } + } + return descriptor; +} + +const Message* +ExtensionSet::GetPrototype(const Descriptor* message_type) const { + return message_factory_->GetPrototype(message_type); +} + +// =================================================================== +// Constructors and basic methods. + +ExtensionSet::ExtensionSet(const Descriptor* extendee, + const DescriptorPool* pool, + MessageFactory* factory) + : extendee_(extendee), + descriptor_pool_(pool), + message_factory_(factory) { +} + +ExtensionSet::~ExtensionSet() { + for (map::iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + iter->second.Free(); + } +} + +void ExtensionSet::AppendToList(vector* output) const { + for (map::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + bool has = false; + if (iter->second.descriptor->is_repeated()) { + has = iter->second.GetSize() > 0; + } else { + has = !iter->second.is_cleared; + } + + if (has) { + output->push_back(iter->second.descriptor); + } + } +} + +bool ExtensionSet::Has(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) return false; + GOOGLE_DCHECK(!iter->second.descriptor->is_repeated()); + return !iter->second.is_cleared; +} + +int ExtensionSet::ExtensionSize(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) return false; + return iter->second.GetSize(); +} + +void ExtensionSet::ClearExtension(int number) { + map::iterator iter = extensions_.find(number); + if (iter == extensions_.end()) return; + iter->second.Clear(); +} + +// =================================================================== +// Field accessors + +#define GOOGLE_DCHECK_TYPE(DESCRIPTOR, LABEL, CPPTYPE) \ + GOOGLE_DCHECK_EQ(DESCRIPTOR->label(), FieldDescriptor::LABEL_##LABEL); \ + GOOGLE_DCHECK_EQ(DESCRIPTOR->cpp_type(), FieldDescriptor::CPPTYPE_##CPPTYPE) + +// ------------------------------------------------------------------- +// Primitives + +#define PRIMITIVE_ACCESSORS(UPPERCASE, LOWERCASE, CAMELCASE) \ + \ +LOWERCASE ExtensionSet::Get##CAMELCASE(int number) const { \ + map::const_iterator iter = extensions_.find(number); \ + if (iter == extensions_.end()) { \ + /* Not present. Return the default value. */ \ + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); \ + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, UPPERCASE); \ + return descriptor->default_value_##LOWERCASE(); \ + } else { \ + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, UPPERCASE); \ + return iter->second.LOWERCASE##_value; \ + } \ +} \ + \ +void ExtensionSet::Set##CAMELCASE(int number, LOWERCASE value) { \ + Extension* extension = &extensions_[number]; \ + if (extension->descriptor == NULL) { \ + /* Not previoulsy present. Initialize it. */ \ + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); \ + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, UPPERCASE); \ + extension->descriptor = descriptor; \ + extension->LOWERCASE##_value = descriptor->default_value_##LOWERCASE(); \ + } else { \ + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, UPPERCASE); \ + extension->is_cleared = false; \ + } \ + extension->LOWERCASE##_value = value; \ +} \ + \ +LOWERCASE ExtensionSet::GetRepeated##CAMELCASE(int number, int index) const { \ + map::const_iterator iter = extensions_.find(number); \ + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; \ + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, UPPERCASE); \ + return iter->second.repeated_##LOWERCASE##_value->Get(index); \ +} \ + \ +void ExtensionSet::SetRepeated##CAMELCASE( \ + int number, int index, LOWERCASE value) { \ + map::iterator iter = extensions_.find(number); \ + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; \ + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, UPPERCASE); \ + iter->second.repeated_##LOWERCASE##_value->Set(index, value); \ +} \ + \ +void ExtensionSet::Add##CAMELCASE(int number, LOWERCASE value) { \ + Extension* extension = &extensions_[number]; \ + if (extension->descriptor == NULL) { \ + /* Not previoulsy present. Initialize it. */ \ + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); \ + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, UPPERCASE); \ + extension->repeated_##LOWERCASE##_value = new RepeatedField(); \ + extension->descriptor = descriptor; \ + } else { \ + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, UPPERCASE); \ + } \ + extension->repeated_##LOWERCASE##_value->Add(value); \ +} + +PRIMITIVE_ACCESSORS( INT32, int32, Int32) +PRIMITIVE_ACCESSORS( INT64, int64, Int64) +PRIMITIVE_ACCESSORS(UINT32, uint32, UInt32) +PRIMITIVE_ACCESSORS(UINT64, uint64, UInt64) +PRIMITIVE_ACCESSORS( FLOAT, float, Float) +PRIMITIVE_ACCESSORS(DOUBLE, double, Double) +PRIMITIVE_ACCESSORS( BOOL, bool, Bool) + +#undef PRIMITIVE_ACCESSORS + +// ------------------------------------------------------------------- +// Enums + +int ExtensionSet::GetEnum(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) { + // Not present. Return the default value. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, ENUM); + return descriptor->default_value_enum()->number(); + } else { + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, ENUM); + return iter->second.enum_value; + } +} + +void ExtensionSet::SetEnum(int number, int value) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, ENUM); + extension->descriptor = descriptor; + extension->enum_value = descriptor->default_value_enum()->number(); + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, ENUM); + extension->is_cleared = false; + } + GOOGLE_DCHECK(extension->descriptor->enum_type()->FindValueByNumber(value) != NULL); + extension->enum_value = value; +} + +int ExtensionSet::GetRepeatedEnum(int number, int index) const { + map::const_iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, ENUM); + return iter->second.repeated_enum_value->Get(index); +} + +void ExtensionSet::SetRepeatedEnum(int number, int index, int value) { + map::iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, ENUM); + GOOGLE_DCHECK(iter->second.descriptor->enum_type() + ->FindValueByNumber(value) != NULL); + iter->second.repeated_enum_value->Set(index, value); +} + +void ExtensionSet::AddEnum(int number, int value) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, ENUM); + extension->repeated_enum_value = new RepeatedField(); + extension->descriptor = descriptor; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, ENUM); + } + GOOGLE_DCHECK(extension->descriptor->enum_type()->FindValueByNumber(value) != NULL); + extension->repeated_enum_value->Add(value); +} + +// ------------------------------------------------------------------- +// Strings + +const string& ExtensionSet::GetString(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) { + // Not present. Return the default value. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, STRING); + return descriptor->default_value_string(); + } else { + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, STRING); + return *iter->second.string_value; + } +} + +string* ExtensionSet::MutableString(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, STRING); + extension->descriptor = descriptor; + extension->string_value = new string; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, STRING); + extension->is_cleared = false; + } + return extension->string_value; +} + +const string& ExtensionSet::GetRepeatedString(int number, int index) const { + map::const_iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, STRING); + return iter->second.repeated_string_value->Get(index); +} + +string* ExtensionSet::MutableRepeatedString(int number, int index) { + map::iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, STRING); + return iter->second.repeated_string_value->Mutable(index); +} + +string* ExtensionSet::AddString(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, STRING); + extension->repeated_string_value = new RepeatedPtrField(); + extension->descriptor = descriptor; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, STRING); + } + return extension->repeated_string_value->Add(); +} + +// ------------------------------------------------------------------- +// Messages + +const Message& ExtensionSet::GetMessage(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) { + // Not present. Return the default value. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, MESSAGE); + return *GetPrototype(descriptor->message_type()); + } else { + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, MESSAGE); + return *iter->second.message_value; + } +} + +Message* ExtensionSet::MutableMessage(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, MESSAGE); + extension->descriptor = descriptor; + extension->message_value = GetPrototype(descriptor->message_type())->New(); + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, MESSAGE); + extension->is_cleared = false; + } + return extension->message_value; +} + +const Message& ExtensionSet::GetRepeatedMessage(int number, int index) const { + map::const_iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, MESSAGE); + return iter->second.repeated_message_value->Get(index); +} + +Message* ExtensionSet::MutableRepeatedMessage(int number, int index) { + map::iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, MESSAGE); + return iter->second.repeated_message_value->Mutable(index); +} + +Message* ExtensionSet::AddMessage(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, MESSAGE); + extension->repeated_message_value = + new RepeatedPtrField(GetPrototype(descriptor->message_type())); + extension->descriptor = descriptor; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, MESSAGE); + } + return extension->repeated_message_value->Add(); +} + +#undef GOOGLE_DCHECK_TYPE + +// =================================================================== + +void ExtensionSet::Clear() { + for (map::iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + iter->second.Clear(); + } +} + +namespace { + +// A helper function for merging RepeatedFields... +// TODO(kenton): Implement this as a method of RepeatedField? Make generated +// MergeFrom methods use it? + +template +void MergeRepeatedFields(const RepeatedField& source, + RepeatedField* destination) { + destination->Reserve(destination->size() + source.size()); + for (int i = 0; i < source.size(); i++) { + destination->Add(source.Get(i)); + } +} + +void MergeRepeatedFields(const RepeatedPtrField& source, + RepeatedPtrField* destination) { + destination->Reserve(destination->size() + source.size()); + for (int i = 0; i < source.size(); i++) { + destination->Add()->assign(source.Get(i)); + } +} + +void MergeRepeatedFields(const RepeatedPtrField& source, + RepeatedPtrField* destination) { + destination->Reserve(destination->size() + source.size()); + for (int i = 0; i < source.size(); i++) { + destination->Add()->MergeFrom(source.Get(i)); + } +} + +} // namespace + +void ExtensionSet::MergeFrom(const ExtensionSet& other) { + GOOGLE_DCHECK_EQ(extendee_, other.extendee_); + + for (map::const_iterator iter = other.extensions_.begin(); + iter != other.extensions_.end(); ++iter) { + const FieldDescriptor* field = iter->second.descriptor; + if (field->is_repeated()) { + const Extension& other_extension = iter->second; + Extension* extension = &extensions_[iter->first]; + switch (field->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE, REPEATED_TYPE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + if (extension->descriptor == NULL) { \ + extension->descriptor = field; \ + extension->repeated_##LOWERCASE##_value = \ + new REPEATED_TYPE; \ + } \ + MergeRepeatedFields( \ + *other_extension.repeated_##LOWERCASE##_value, \ + extension->repeated_##LOWERCASE##_value); \ + break; + + HANDLE_TYPE( INT32, int32, RepeatedField < int32>); + HANDLE_TYPE( INT64, int64, RepeatedField < int64>); + HANDLE_TYPE( UINT32, uint32, RepeatedField < uint32>); + HANDLE_TYPE( UINT64, uint64, RepeatedField < uint64>); + HANDLE_TYPE( FLOAT, float, RepeatedField < float>); + HANDLE_TYPE( DOUBLE, double, RepeatedField < double>); + HANDLE_TYPE( BOOL, bool, RepeatedField < bool>); + HANDLE_TYPE( ENUM, enum, RepeatedField < int>); + HANDLE_TYPE( STRING, string, RepeatedPtrField< string>); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_MESSAGE: + if (extension->descriptor == NULL) { + extension->descriptor = field; + extension->repeated_message_value = new RepeatedPtrField( + other_extension.repeated_message_value->prototype()); + } + MergeRepeatedFields( + *other_extension.repeated_message_value, + extension->repeated_message_value); + break; + } + } else { + if (!iter->second.is_cleared) { + switch (field->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE, CAMELCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + Set##CAMELCASE(iter->first, iter->second.LOWERCASE##_value); \ + break; + + HANDLE_TYPE( INT32, int32, Int32); + HANDLE_TYPE( INT64, int64, Int64); + HANDLE_TYPE(UINT32, uint32, UInt32); + HANDLE_TYPE(UINT64, uint64, UInt64); + HANDLE_TYPE( FLOAT, float, Float); + HANDLE_TYPE(DOUBLE, double, Double); + HANDLE_TYPE( BOOL, bool, Bool); + HANDLE_TYPE( ENUM, enum, Enum); +#undef HANDLE_TYPE + case FieldDescriptor::CPPTYPE_STRING: + SetString(iter->first, *iter->second.string_value); + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + MutableMessage(iter->first)->MergeFrom(*iter->second.message_value); + break; + } + } + } + } +} + +bool ExtensionSet::IsInitialized() const { + // Extensions are never requried. However, we need to check that all + // embedded messages are initialized. + for (map::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + const Extension& extension = iter->second; + if (extension.descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (extension.descriptor->is_repeated()) { + for (int i = 0; i < extension.repeated_message_value->size(); i++) { + if (!extension.repeated_message_value->Get(i).IsInitialized()) { + return false; + } + } + } else { + if (!extension.is_cleared) { + if (!extension.message_value->IsInitialized()) return false; + } + } + } + } + + return true; +} + +bool ExtensionSet::ParseField(uint32 tag, io::CodedInputStream* input, + Message::Reflection* reflection) { + const FieldDescriptor* field = + FindKnownExtensionByNumber(WireFormat::GetTagFieldNumber(tag)); + + return WireFormat::ParseAndMergeField(tag, field, reflection, input); +} + +bool ExtensionSet::SerializeWithCachedSizes( + int start_field_number, int end_field_number, + const Message::Reflection* reflection, + io::CodedOutputStream* output) const { + map::const_iterator iter; + for (iter = extensions_.lower_bound(start_field_number); + iter != extensions_.end() && iter->first < end_field_number; + ++iter) { + if (!iter->second.SerializeFieldWithCachedSizes(reflection, output)) { + return false; + } + } + + return true; +} + +int ExtensionSet::ByteSize(const Message::Reflection* reflection) const { + int total_size = 0; + + for (map::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + total_size += iter->second.ByteSize(reflection); + } + + return total_size; +} + +// =================================================================== +// Methods of ExtensionSet::Extension + +void ExtensionSet::Extension::Clear() { + if (descriptor->is_repeated()) { + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + repeated_##LOWERCASE##_value->Clear(); \ + break + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE( UINT32, uint32); + HANDLE_TYPE( UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE( DOUBLE, double); + HANDLE_TYPE( BOOL, bool); + HANDLE_TYPE( ENUM, enum); + HANDLE_TYPE( STRING, string); + HANDLE_TYPE(MESSAGE, message); +#undef HANDLE_TYPE + } + } else { + if (!is_cleared) { + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + LOWERCASE##_value = descriptor->default_value_##LOWERCASE(); \ + break + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE(UINT32, uint32); + HANDLE_TYPE(UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE(DOUBLE, double); + HANDLE_TYPE( BOOL, bool); +#undef HANDLE_TYPE + case FieldDescriptor::CPPTYPE_ENUM: + enum_value = descriptor->default_value_enum()->number(); + break; + case FieldDescriptor::CPPTYPE_STRING: + if (descriptor->has_default_value()) { + string_value->assign(descriptor->default_value_string()); + } else { + string_value->clear(); + } + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + message_value->Clear(); + break; + } + + is_cleared = true; + } + } +} + +bool ExtensionSet::Extension::SerializeFieldWithCachedSizes( + const Message::Reflection* reflection, + io::CodedOutputStream* output) const { + if (descriptor->is_repeated() || !is_cleared) { + return WireFormat::SerializeFieldWithCachedSizes( + descriptor, reflection, output); + } else { + return true; + } +} + +int64 ExtensionSet::Extension::ByteSize( + const Message::Reflection* reflection) const { + if (descriptor->is_repeated() || !is_cleared) { + return WireFormat::FieldByteSize(descriptor, reflection); + } else { + // Cleared, non-repeated field. + return 0; + } +} + +int ExtensionSet::Extension::GetSize() const { + GOOGLE_DCHECK(descriptor->is_repeated()); + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + return repeated_##LOWERCASE##_value->size() + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE( UINT32, uint32); + HANDLE_TYPE( UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE( DOUBLE, double); + HANDLE_TYPE( BOOL, bool); + HANDLE_TYPE( ENUM, enum); + HANDLE_TYPE( STRING, string); + HANDLE_TYPE(MESSAGE, message); +#undef HANDLE_TYPE + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return 0; +} + +void ExtensionSet::Extension::Free() { + if (descriptor->is_repeated()) { + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + delete repeated_##LOWERCASE##_value; \ + break + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE( UINT32, uint32); + HANDLE_TYPE( UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE( DOUBLE, double); + HANDLE_TYPE( BOOL, bool); + HANDLE_TYPE( ENUM, enum); + HANDLE_TYPE( STRING, string); + HANDLE_TYPE(MESSAGE, message); +#undef HANDLE_TYPE + } + } else { + switch (descriptor->cpp_type()) { + case FieldDescriptor::CPPTYPE_STRING: + delete string_value; + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + delete message_value; + break; + default: + break; + } + } +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h new file mode 100644 index 00000000..902ec736 --- /dev/null +++ b/src/google/protobuf/extension_set.h @@ -0,0 +1,555 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_EXTENSION_SET_H__ +#define GOOGLE_PROTOBUF_EXTENSION_SET_H__ + +#include +#include +#include +#include +#include + +#include + +namespace google { +namespace protobuf { + class Descriptor; // descriptor.h + class FieldDescriptor; // descriptor.h + class DescriptorPool; // descriptor.h + class Message; // message.h + class MessageFactory; // message.h + namespace io { + class CodedInputStream; // coded_stream.h + class CodedOutputStream; // coded_stream.h + } + template class RepeatedField; // repeated_field.h + template class RepeatedPtrField; // repeated_field.h +} + +namespace protobuf { +namespace internal { + +// This is an internal helper class intended for use within the protocol buffer +// library and generated classes. Clients should not use it directly. Instead, +// use the generated accessors such as GetExtension() of the class being +// extended. +// +// This class manages extensions for a protocol message object. The +// message's HasExtension(), GetExtension(), MutableExtension(), and +// ClearExtension() methods are just thin wrappers around the embedded +// ExtensionSet. When parsing, if a tag number is encountered which is +// inside one of the message type's extension ranges, the tag is passed +// off to the ExtensionSet for parsing. Etc. +class LIBPROTOBUF_EXPORT ExtensionSet { + public: + // Construct an ExtensionSet. + // extendee: Descriptor for the type being extended. + // pool: DescriptorPool to search for extension definitions. + // factory: MessageFactory used to construct implementations of messages + // for extensions with message type. This factory must be able + // to construct any message type found in "pool". + // All three objects remain property of the caller and must outlive the + // ExtensionSet. + ExtensionSet(const Descriptor* extendee, + const DescriptorPool* pool, + MessageFactory* factory); + + ~ExtensionSet(); + + // Search for a known (compiled-in) extension of this type by name or number. + // Returns NULL if no extension is known. + const FieldDescriptor* FindKnownExtensionByName(const string& name) const; + const FieldDescriptor* FindKnownExtensionByNumber(int number) const; + + // Add all fields which are currently present to the given vector. This + // is useful to implement Message::Reflection::ListFields(). + void AppendToList(vector* output) const; + + // ================================================================= + // Accessors + // + // Generated message classes include type-safe templated wrappers around + // these methods. Generally you should use those rather than call these + // directly, unless you are doing low-level memory management. + // + // When calling any of these accessors, the extension number requested + // MUST exist in the DescriptorPool provided to the constructor. Otheriwse, + // the method will fail an assert. Normally, though, you would not call + // these directly; you would either call the generated accessors of your + // message class (e.g. GetExtension()) or you would call the accessors + // of the reflection interface. In both cases, it is impossible to + // trigger this assert failure: the generated accessors only accept + // linked-in extension types as parameters, while the Reflection interface + // requires you to provide the FieldDescriptor describing the extension. + // + // When calling any of these accessors, a protocol-compiler-generated + // implementation of the extension corresponding to the number MUST + // be linked in, and the FieldDescriptor used to refer to it MUST be + // the one generated by that linked-in code. Otherwise, the method will + // die on an assert failure. The message objects returned by the message + // accessors are guaranteed to be of the correct linked-in type. + // + // These methods pretty much match Message::Reflection except that: + // - They're not virtual. + // - They identify fields by number rather than FieldDescriptors. + // - They identify enum values using integers rather than descriptors. + // - Strings provide Mutable() in addition to Set() accessors. + + bool Has(int number) const; + int ExtensionSize(int number) const; // Size of a repeated extension. + void ClearExtension(int number); + + // singular fields ------------------------------------------------- + + int32 GetInt32 (int number) const; + int64 GetInt64 (int number) const; + uint32 GetUInt32(int number) const; + uint64 GetUInt64(int number) const; + float GetFloat (int number) const; + double GetDouble(int number) const; + bool GetBool (int number) const; + int GetEnum (int number) const; + const string & GetString (int number) const; + const Message& GetMessage(int number) const; + + void SetInt32 (int number, int32 value); + void SetInt64 (int number, int64 value); + void SetUInt32(int number, uint32 value); + void SetUInt64(int number, uint64 value); + void SetFloat (int number, float value); + void SetDouble(int number, double value); + void SetBool (int number, bool value); + void SetEnum (int number, int value); + void SetString(int number, const string& value); + string * MutableString (int number); + Message* MutableMessage(int number); + + // repeated fields ------------------------------------------------- + + int32 GetRepeatedInt32 (int number, int index) const; + int64 GetRepeatedInt64 (int number, int index) const; + uint32 GetRepeatedUInt32(int number, int index) const; + uint64 GetRepeatedUInt64(int number, int index) const; + float GetRepeatedFloat (int number, int index) const; + double GetRepeatedDouble(int number, int index) const; + bool GetRepeatedBool (int number, int index) const; + int GetRepeatedEnum (int number, int index) const; + const string & GetRepeatedString (int number, int index) const; + const Message& GetRepeatedMessage(int number, int index) const; + + void SetRepeatedInt32 (int number, int index, int32 value); + void SetRepeatedInt64 (int number, int index, int64 value); + void SetRepeatedUInt32(int number, int index, uint32 value); + void SetRepeatedUInt64(int number, int index, uint64 value); + void SetRepeatedFloat (int number, int index, float value); + void SetRepeatedDouble(int number, int index, double value); + void SetRepeatedBool (int number, int index, bool value); + void SetRepeatedEnum (int number, int index, int value); + void SetRepeatedString(int number, int index, const string& value); + string * MutableRepeatedString (int number, int index); + Message* MutableRepeatedMessage(int number, int index); + + void AddInt32 (int number, int32 value); + void AddInt64 (int number, int64 value); + void AddUInt32(int number, uint32 value); + void AddUInt64(int number, uint64 value); + void AddFloat (int number, float value); + void AddDouble(int number, double value); + void AddBool (int number, bool value); + void AddEnum (int number, int value); + void AddString(int number, const string& value); + string * AddString (int number); + Message* AddMessage(int number); + + // ----------------------------------------------------------------- + // TODO(kenton): Hardcore memory management accessors + + // ================================================================= + // convenience methods for implementing methods of Message + // + // These could all be implemented in terms of the other methods of this + // class, but providing them here helps keep the generated code size down. + + void Clear(); + void MergeFrom(const ExtensionSet& other); + bool IsInitialized() const; + + // These parsing and serialization functions all want a pointer to the + // reflection interface because they hand off the actual work to WireFormat, + // which works in terms of a reflection interface. Yes, this means there + // are some redundant virtual function calls that end up being made, but + // it probably doesn't matter much in practice, and the alternative would + // involve reproducing a lot of WireFormat's functionality. + + // Parses a single extension from the input. The input should start out + // positioned immediately after the tag. + bool ParseField(uint32 tag, io::CodedInputStream* input, + Message::Reflection* reflection); + + // Write all extension fields with field numbers in the range + // [start_field_number, end_field_number) + // to the output stream, using the cached sizes computed when ByteSize() was + // last called. Note that the range bounds are inclusive-exclusive. + bool SerializeWithCachedSizes(int start_field_number, + int end_field_number, + const Message::Reflection* reflection, + io::CodedOutputStream* output) const; + + // Returns the total serialized size of all the extensions. + int ByteSize(const Message::Reflection* reflection) const; + + private: + // Like FindKnownExtension(), but GOOGLE_CHECK-fail if not found. + const FieldDescriptor* FindKnownExtensionOrDie(int number) const; + + // Get the prototype for the message. + const Message* GetPrototype(const Descriptor* message_type) const; + + struct Extension { + union { + int32 int32_value; + int64 int64_value; + uint32 uint32_value; + uint64 uint64_value; + float float_value; + double double_value; + bool bool_value; + int enum_value; + string* string_value; + Message* message_value; + + RepeatedField * repeated_int32_value; + RepeatedField * repeated_int64_value; + RepeatedField * repeated_uint32_value; + RepeatedField * repeated_uint64_value; + RepeatedField * repeated_float_value; + RepeatedField * repeated_double_value; + RepeatedField * repeated_bool_value; + RepeatedField * repeated_enum_value; + RepeatedPtrField* repeated_string_value; + RepeatedPtrField* repeated_message_value; + }; + + const FieldDescriptor* descriptor; + + // For singular types, indicates if the extension is "cleared". This + // happens when an extension is set and then later cleared by the caller. + // We want to keep the Extension object around for reuse, so instead of + // removing it from the map, we just set is_cleared = true. This has no + // meaning for repeated types; for those, the size of the RepeatedField + // simply becomes zero when cleared. + bool is_cleared; + + Extension(): descriptor(NULL), is_cleared(false) {} + + // Some helper methods for operations on a single Extension. + bool SerializeFieldWithCachedSizes( + const Message::Reflection* reflection, + io::CodedOutputStream* output) const; + int64 ByteSize(const Message::Reflection* reflection) const; + void Clear(); + int GetSize() const; + void Free(); + }; + + // The Extension struct is small enough to be passed by value, so we use it + // directly as the value type in the map rather than use pointers. We use + // a map rather than hash_map here because we expect most ExtensionSets will + // only contain a small number of extensions whereas hash_map is optimized + // for 100 elements or more. Also, we want AppendToList() to order fields + // by field number. + map extensions_; + const Descriptor* extendee_; + const DescriptorPool* descriptor_pool_; + MessageFactory* message_factory_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionSet); +}; + +// These are just for convenience... +inline void ExtensionSet::SetString(int number, const string& value) { + MutableString(number)->assign(value); +} +inline void ExtensionSet::SetRepeatedString(int number, int index, + const string& value) { + MutableRepeatedString(number, index)->assign(value); +} +inline void ExtensionSet::AddString(int number, const string& value) { + AddString(number)->assign(value); +} + +// =================================================================== +// Implementation details +// +// DO NOT DEPEND ON ANYTHING BELOW THIS POINT. This is for use from +// generated code only. + +// ------------------------------------------------------------------- +// Template magic + +// First we have a set of classes representing "type traits" for different +// field types. A type traits class knows how to implement basic accessors +// for extensions of a particular type given an ExtensionSet. The signature +// for a type traits class looks like this: +// +// class TypeTraits { +// public: +// typedef ? ConstType; +// typedef ? MutableType; +// +// static inline ConstType Get(int number, const ExtensionSet& set); +// static inline void Set(int number, ConstType value, ExtensionSet* set); +// static inline MutableType Mutable(int number, ExtensionSet* set); +// +// // Variants for repeated fields. +// static inline ConstType Get(int number, const ExtensionSet& set, +// int index); +// static inline void Set(int number, int index, +// ConstType value, ExtensionSet* set); +// static inline MutableType Mutable(int number, int index, +// ExtensionSet* set); +// static inline void Add(int number, ConstType value, ExtensionSet* set); +// static inline MutableType Add(int number, ExtensionSet* set); +// }; +// +// Not all of these methods make sense for all field types. For example, the +// "Mutable" methods only make sense for strings and messages, and the +// repeated methods only make sense for repeated types. So, each type +// traits class implements only the set of methods from this signature that it +// actually supports. This will cause a compiler error if the user tries to +// access an extension using a method that doesn't make sense for its type. +// For example, if "foo" is an extension of type "optional int32", then if you +// try to write code like: +// my_message.MutableExtension(foo) +// you will get a compile error because PrimitiveTypeTraits does not +// have a "Mutable()" method. + +// ------------------------------------------------------------------- +// PrimitiveTypeTraits + +// Since the ExtensionSet has different methods for each primitive type, +// we must explicitly define the methods of the type traits class for each +// known type. +template +class PrimitiveTypeTraits { + public: + typedef Type ConstType; + + static inline ConstType Get(int number, const ExtensionSet& set); + static inline void Set(int number, ConstType value, ExtensionSet* set); +}; + +template +class RepeatedPrimitiveTypeTraits { + public: + typedef Type ConstType; + + static inline Type Get(int number, const ExtensionSet& set, int index); + static inline void Set(int number, int index, Type value, ExtensionSet* set); + static inline void Add(int number, Type value, ExtensionSet* set); +}; + +#define PROTOBUF_DEFINE_PRIMITIVE_TYPE(TYPE, METHOD) \ +template<> inline TYPE PrimitiveTypeTraits::Get( \ + int number, const ExtensionSet& set) { \ + return set.Get##METHOD(number); \ +} \ +template<> inline void PrimitiveTypeTraits::Set( \ + int number, ConstType value, ExtensionSet* set) { \ + set->Set##METHOD(number, value); \ +} \ + \ +template<> inline TYPE RepeatedPrimitiveTypeTraits::Get( \ + int number, const ExtensionSet& set, int index) { \ + return set.GetRepeated##METHOD(number, index); \ +} \ +template<> inline void RepeatedPrimitiveTypeTraits::Set( \ + int number, int index, ConstType value, ExtensionSet* set) { \ + set->SetRepeated##METHOD(number, index, value); \ +} \ +template<> inline void RepeatedPrimitiveTypeTraits::Add( \ + int number, ConstType value, ExtensionSet* set) { \ + set->Add##METHOD(number, value); \ +} + +PROTOBUF_DEFINE_PRIMITIVE_TYPE( int32, Int32) +PROTOBUF_DEFINE_PRIMITIVE_TYPE( int64, Int64) +PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint32, UInt32) +PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint64, UInt64) +PROTOBUF_DEFINE_PRIMITIVE_TYPE( float, Float) +PROTOBUF_DEFINE_PRIMITIVE_TYPE(double, Double) +PROTOBUF_DEFINE_PRIMITIVE_TYPE( bool, Bool) + +#undef PROTOBUF_DEFINE_PRIMITIVE_TYPE + +// ------------------------------------------------------------------- +// StringTypeTraits + +// Strings support both Set() and Mutable(). +class LIBPROTOBUF_EXPORT StringTypeTraits { + public: + typedef const string& ConstType; + typedef string* MutableType; + + static inline const string& Get(int number, const ExtensionSet& set) { + return set.GetString(number); + } + static inline void Set(int number, const string& value, ExtensionSet* set) { + set->SetString(number, value); + } + static inline string* Mutable(int number, ExtensionSet* set) { + return set->MutableString(number); + } +}; + +class LIBPROTOBUF_EXPORT RepeatedStringTypeTraits { + public: + typedef const string& ConstType; + typedef string* MutableType; + + static inline const string& Get(int number, const ExtensionSet& set, + int index) { + return set.GetRepeatedString(number, index); + } + static inline void Set(int number, int index, + const string& value, ExtensionSet* set) { + set->SetRepeatedString(number, index, value); + } + static inline string* Mutable(int number, int index, ExtensionSet* set) { + return set->MutableRepeatedString(number, index); + } + static inline void Add(int number, const string& value, ExtensionSet* set) { + set->AddString(number, value); + } + static inline string* Add(int number, ExtensionSet* set) { + return set->AddString(number); + } +}; + +// ------------------------------------------------------------------- +// EnumTypeTraits + +// ExtensionSet represents enums using integers internally, so we have to +// static_cast around. +template +class EnumTypeTraits { + public: + typedef Type ConstType; + + static inline ConstType Get(int number, const ExtensionSet& set) { + return static_cast(set.GetEnum(number)); + } + static inline void Set(int number, ConstType value, ExtensionSet* set) { + set->SetEnum(number, value); + } +}; + +template +class RepeatedEnumTypeTraits { + public: + typedef Type ConstType; + + static inline ConstType Get(int number, const ExtensionSet& set, int index) { + return static_cast(set.GetRepeatedEnum(number, index)); + } + static inline void Set(int number, int index, + ConstType value, ExtensionSet* set) { + set->SetRepeatedEnum(number, index, value); + } + static inline void Add(int number, ConstType value, ExtensionSet* set) { + set->AddEnum(number, value); + } +}; + +// ------------------------------------------------------------------- +// MessageTypeTraits + +// ExtensionSet guarantees that when manipulating extensions with message +// types, the implementation used will be the compiled-in class representing +// that type. So, we can static_cast down to the exact type we expect. +template +class MessageTypeTraits { + public: + typedef const Type& ConstType; + typedef Type* MutableType; + + static inline ConstType Get(int number, const ExtensionSet& set) { + return static_cast(set.GetMessage(number)); + } + static inline MutableType Mutable(int number, ExtensionSet* set) { + return static_cast(set->MutableMessage(number)); + } +}; + +template +class RepeatedMessageTypeTraits { + public: + typedef const Type& ConstType; + typedef Type* MutableType; + + static inline ConstType Get(int number, const ExtensionSet& set, int index) { + return static_cast(set.GetRepeatedMessage(number, index)); + } + static inline MutableType Mutable(int number, int index, ExtensionSet* set) { + return static_cast(set->MutableRepeatedMessage(number, index)); + } + static inline MutableType Add(int number, ExtensionSet* set) { + return static_cast(set->AddMessage(number)); + } +}; + +// ------------------------------------------------------------------- +// ExtensionIdentifier + +// This is the type of actual extension objects. E.g. if you have: +// extends Foo with optional int32 bar = 1234; +// then "bar" will be defined in C++ as: +// ExtensionIdentifier> bar(1234); +// +// Note that we could, in theory, supply the field number as a template +// parameter, and thus make an instance of ExtensionIdentifier have no +// actual contents. However, if we did that, then using at extension +// identifier would not necessarily cause the compiler to output any sort +// of reference to any simple defined in the extension's .pb.o file. Some +// linkers will actually drop object files that are not explicitly referenced, +// but that would be bad because it would cause this extension to not be +// registered at static initialization, and therefore using it would crash. + +template +class ExtensionIdentifier { + public: + typedef TypeTraitsType TypeTraits; + typedef ExtendeeType Extendee; + + ExtensionIdentifier(int number): number_(number) {} + inline int number() const { return number_; } + private: + const int number_; +}; + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_EXTENSION_SET_H__ diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc new file mode 100644 index 00000000..c10f8900 --- /dev/null +++ b/src/google/protobuf/extension_set_unittest.cc @@ -0,0 +1,195 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { +namespace { + +// This test closely mirrors google/protobuf/compiler/cpp/unittest.cc +// except that it uses extensions rather than regular fields. + +TEST(ExtensionSetTest, Defaults) { + // Check that all default values are set correctly in the initial message. + unittest::TestAllExtensions message; + + TestUtil::ExpectExtensionsClear(message); + + // Messages should return pointers to default instances until first use. + // (This is not checked by ExpectClear() since it is not actually true after + // the fields have been set and then cleared.) + EXPECT_EQ(&unittest::OptionalGroup_extension::default_instance(), + &message.GetExtension(unittest::optionalgroup_extension)); + EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.GetExtension(unittest::optional_nested_message_extension)); + EXPECT_EQ(&unittest::ForeignMessage::default_instance(), + &message.GetExtension( + unittest::optional_foreign_message_extension)); + EXPECT_EQ(&unittest_import::ImportMessage::default_instance(), + &message.GetExtension(unittest::optional_import_message_extension)); +} + +TEST(ExtensionSetTest, Accessors) { + // Set every field to a unique value then go back and check all those + // values. + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + TestUtil::ExpectAllExtensionsSet(message); + + TestUtil::ModifyRepeatedExtensions(&message); + TestUtil::ExpectRepeatedExtensionsModified(message); +} + +TEST(ExtensionSetTest, Clear) { + // Set every field to a unique value, clear the message, then check that + // it is cleared. + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + message.Clear(); + TestUtil::ExpectExtensionsClear(message); + + // Unlike with the defaults test, we do NOT expect that requesting embedded + // messages will return a pointer to the default instance. Instead, they + // should return the objects that were created when mutable_blah() was + // called. + EXPECT_NE(&unittest::OptionalGroup_extension::default_instance(), + &message.GetExtension(unittest::optionalgroup_extension)); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.GetExtension(unittest::optional_nested_message_extension)); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.GetExtension( + unittest::optional_foreign_message_extension)); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.GetExtension(unittest::optional_import_message_extension)); +} + +TEST(ExtensionSetTest, ClearOneField) { + // Set every field to a unique value, then clear one value and insure that + // only that one value is cleared. + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + int64 original_value = + message.GetExtension(unittest::optional_int64_extension); + + // Clear the field and make sure it shows up as cleared. + message.ClearExtension(unittest::optional_int64_extension); + EXPECT_FALSE(message.HasExtension(unittest::optional_int64_extension)); + EXPECT_EQ(0, message.GetExtension(unittest::optional_int64_extension)); + + // Other adjacent fields should not be cleared. + EXPECT_TRUE(message.HasExtension(unittest::optional_int32_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_uint32_extension)); + + // Make sure if we set it again, then all fields are set. + message.SetExtension(unittest::optional_int64_extension, original_value); + TestUtil::ExpectAllExtensionsSet(message); +} + +TEST(ExtensionSetTest, CopyFrom) { + unittest::TestAllExtensions message1, message2; + string data; + + TestUtil::SetAllExtensions(&message1); + message2.CopyFrom(message1); + TestUtil::ExpectAllExtensionsSet(message2); +} + +TEST(ExtensionSetTest, Serialization) { + // Serialize as TestAllExtensions and parse as TestAllTypes to insure wire + // compatibility of extensions. + unittest::TestAllExtensions source; + unittest::TestAllTypes destination; + string data; + + TestUtil::SetAllExtensions(&source); + source.SerializeToString(&data); + EXPECT_TRUE(destination.ParseFromString(data)); + TestUtil::ExpectAllFieldsSet(destination); +} + +TEST(ExtensionSetTest, Parsing) { + // Serialize as TestAllTypes and parse as TestAllExtensions. + unittest::TestAllTypes source; + unittest::TestAllExtensions destination; + string data; + + TestUtil::SetAllFields(&source); + source.SerializeToString(&data); + EXPECT_TRUE(destination.ParseFromString(data)); + TestUtil::ExpectAllExtensionsSet(destination); +} + +TEST(ExtensionSetTest, IsInitialized) { + // Test that IsInitialized() returns false if required fields in nested + // extensions are missing. + unittest::TestAllExtensions message; + + EXPECT_TRUE(message.IsInitialized()); + + message.MutableExtension(unittest::TestRequired::single); + EXPECT_FALSE(message.IsInitialized()); + + message.MutableExtension(unittest::TestRequired::single)->set_a(1); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::single)->set_b(2); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::single)->set_c(3); + EXPECT_TRUE(message.IsInitialized()); + + message.AddExtension(unittest::TestRequired::multi); + EXPECT_FALSE(message.IsInitialized()); + + message.MutableExtension(unittest::TestRequired::multi, 0)->set_a(1); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_b(2); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_c(3); + EXPECT_TRUE(message.IsInitialized()); +} + +TEST(ExtensionSetTest, MutableString) { + // Test the mutable string accessors. + unittest::TestAllExtensions message; + + message.MutableExtension(unittest::optional_string_extension)->assign("foo"); + EXPECT_TRUE(message.HasExtension(unittest::optional_string_extension)); + EXPECT_EQ("foo", message.GetExtension(unittest::optional_string_extension)); + + message.AddExtension(unittest::repeated_string_extension)->assign("bar"); + ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_string_extension)); + EXPECT_EQ("bar", + message.GetExtension(unittest::repeated_string_extension, 0)); +} + +} // namespace +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc new file mode 100644 index 00000000..ec17572b --- /dev/null +++ b/src/google/protobuf/generated_message_reflection.cc @@ -0,0 +1,665 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +namespace { const string kEmptyString; } + +// =================================================================== +// Helpers for reporting usage errors (e.g. trying to use GetInt32() on +// a string field). + +namespace { + +void ReportReflectionUsageError( + const Descriptor* descriptor, const FieldDescriptor* field, + const char* method, const char* description) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Message::Reflection::" << method << "\n" + " Message type: " << descriptor->full_name() << "\n" + " Field : " << field->full_name() << "\n" + " Problem : " << description; +} + +const char* cpptype_names_[FieldDescriptor::MAX_CPPTYPE + 1] = { + "INVALID_CPPTYPE", + "CPPTYPE_INT32", + "CPPTYPE_INT64", + "CPPTYPE_UINT32", + "CPPTYPE_UINT64", + "CPPTYPE_DOUBLE", + "CPPTYPE_FLOAT", + "CPPTYPE_BOOL", + "CPPTYPE_ENUM", + "CPPTYPE_STRING", + "CPPTYPE_MESSAGE" +}; + +static void ReportReflectionUsageTypeError( + const Descriptor* descriptor, const FieldDescriptor* field, + const char* method, + FieldDescriptor::CppType expected_type) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Message::Reflection::" << method << "\n" + " Message type: " << descriptor->full_name() << "\n" + " Field : " << field->full_name() << "\n" + " Problem : Field is not the right type for this message:\n" + " Expected : " << cpptype_names_[expected_type] << "\n" + " Field type: " << cpptype_names_[field->cpp_type()]; +} + +static void ReportReflectionUsageEnumTypeError( + const Descriptor* descriptor, const FieldDescriptor* field, + const char* method, const EnumValueDescriptor* value) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Message::Reflection::" << method << "\n" + " Message type: " << descriptor->full_name() << "\n" + " Field : " << field->full_name() << "\n" + " Problem : Enum value did not match field type:\n" + " Expected : " << field->enum_type()->full_name() << "\n" + " Actual : " << value->full_name(); +} + +#define USAGE_CHECK(CONDITION, METHOD, ERROR_DESCRIPTION) \ + if (!(CONDITION)) \ + ReportReflectionUsageError(descriptor_, field, #METHOD, ERROR_DESCRIPTION) +#define USAGE_CHECK_EQ(A, B, METHOD, ERROR_DESCRIPTION) \ + USAGE_CHECK((A) == (B), METHOD, ERROR_DESCRIPTION) +#define USAGE_CHECK_NE(A, B, METHOD, ERROR_DESCRIPTION) \ + USAGE_CHECK((A) != (B), METHOD, ERROR_DESCRIPTION) + +#define USAGE_CHECK_TYPE(METHOD, CPPTYPE) \ + if (field->cpp_type() != FieldDescriptor::CPPTYPE_##CPPTYPE) \ + ReportReflectionUsageTypeError(descriptor_, field, #METHOD, \ + FieldDescriptor::CPPTYPE_##CPPTYPE) + +#define USAGE_CHECK_ENUM_VALUE(METHOD) \ + if (value->type() != field->enum_type()) \ + ReportReflectionUsageEnumTypeError(descriptor_, field, #METHOD, value) + +#define USAGE_CHECK_MESSAGE_TYPE(METHOD) \ + USAGE_CHECK_EQ(field->containing_type(), descriptor_, \ + METHOD, "Field does not match message type."); +#define USAGE_CHECK_SINGULAR(METHOD) \ + USAGE_CHECK_NE(field->label(), FieldDescriptor::LABEL_REPEATED, METHOD, \ + "Field is repeated; the method requires a singular field.") +#define USAGE_CHECK_REPEATED(METHOD) \ + USAGE_CHECK_EQ(field->label(), FieldDescriptor::LABEL_REPEATED, METHOD, \ + "Field is singular; the method requires a repeated field.") + +#define USAGE_CHECK_ALL(METHOD, LABEL, CPPTYPE) \ + USAGE_CHECK_MESSAGE_TYPE(METHOD); \ + USAGE_CHECK_##LABEL(METHOD); \ + USAGE_CHECK_TYPE(METHOD, CPPTYPE) + +} // namespace + +// =================================================================== + +GeneratedMessageReflection::GeneratedMessageReflection( + const Descriptor* descriptor, + void* base, const void* default_base, + const int offsets[], uint32 has_bits[], + ExtensionSet* extensions) + : descriptor_ (descriptor), + base_ (base), + default_base_(default_base), + offsets_ (offsets), + has_bits_ (has_bits), + extensions_ (extensions) { +} + +GeneratedMessageReflection::~GeneratedMessageReflection() {} + +const UnknownFieldSet& GeneratedMessageReflection::GetUnknownFields() const { + return unknown_fields_; +} +UnknownFieldSet* GeneratedMessageReflection::MutableUnknownFields() { + return &unknown_fields_; +} + +// ------------------------------------------------------------------- + +bool GeneratedMessageReflection::HasField(const FieldDescriptor* field) const { + USAGE_CHECK_MESSAGE_TYPE(HasField); + USAGE_CHECK_SINGULAR(HasField); + + if (field->is_extension()) { + return extensions_->Has(field->number()); + } else { + return HasBit(field); + } +} + +int GeneratedMessageReflection::FieldSize(const FieldDescriptor* field) const { + USAGE_CHECK_MESSAGE_TYPE(HasField); + USAGE_CHECK_REPEATED(HasField); + + if (field->is_extension()) { + return extensions_->ExtensionSize(field->number()); + } else { + return GetRaw(field).GenericSize(); + } +} + +void GeneratedMessageReflection::ClearField(const FieldDescriptor* field) { + USAGE_CHECK_MESSAGE_TYPE(ClearField); + + if (field->is_extension()) { + extensions_->ClearExtension(field->number()); + } else if (!field->is_repeated()) { + if (HasBit(field)) { + ClearBit(field); + + // We need to set the field back to its default value. + switch (field->cpp_type()) { +#define CLEAR_TYPE(CPPTYPE, TYPE) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + *MutableRaw(field) = field->default_value_##TYPE(); \ + break; + + CLEAR_TYPE(INT32 , int32 ); + CLEAR_TYPE(INT64 , int64 ); + CLEAR_TYPE(UINT32, uint32); + CLEAR_TYPE(UINT64, uint64); + CLEAR_TYPE(FLOAT , float ); + CLEAR_TYPE(DOUBLE, double); + CLEAR_TYPE(BOOL , bool ); +#undef CLEAR_TYPE + + case FieldDescriptor::CPPTYPE_ENUM: + *MutableRaw(field) = field->default_value_enum()->number(); + break; + + case FieldDescriptor::CPPTYPE_STRING: { + const string* default_ptr = DefaultRaw(field); + string** value = MutableRaw(field); + if (*value != default_ptr) { + if (field->has_default_value()) { + (*value)->assign(field->default_value_string()); + } else { + (*value)->clear(); + } + } + break; + } + + case FieldDescriptor::CPPTYPE_MESSAGE: + (*MutableRaw(field))->Clear(); + break; + } + } + } else { + MutableRaw(field)->GenericClear(); + } +} + +namespace { +// Comparison functor for sorting FieldDescriptors by field number. +struct FieldNumberSorter { + bool operator()(const FieldDescriptor* left, + const FieldDescriptor* right) const { + return left->number() < right->number(); + } +}; +} // namespace + +void GeneratedMessageReflection::ListFields( + vector* output) const { + output->clear(); + + // Optimization: The default instance never has any fields set. + if (base_ == default_base_) return; + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->is_repeated()) { + if (GetRaw(field).GenericSize() > 0) { + output->push_back(field); + } + } else { + if (HasBit(field)) { + output->push_back(field); + } + } + } + + if (extensions_ != NULL) { + extensions_->AppendToList(output); + } + + // ListFields() must sort output by field number. + sort(output->begin(), output->end(), FieldNumberSorter()); +} + +// ------------------------------------------------------------------- + +#undef DEFINE_PRIMITIVE_ACCESSORS +#define DEFINE_PRIMITIVE_ACCESSORS(TYPENAME, TYPE, PASSTYPE, CPPTYPE) \ + PASSTYPE GeneratedMessageReflection::Get##TYPENAME( \ + const FieldDescriptor* field) const { \ + USAGE_CHECK_ALL(Get##TYPENAME, SINGULAR, CPPTYPE); \ + if (field->is_extension()) { \ + return extensions_->Get##TYPENAME(field->number()); \ + } else { \ + return GetField(field); \ + } \ + } \ + \ + void GeneratedMessageReflection::Set##TYPENAME( \ + const FieldDescriptor* field, PASSTYPE value) { \ + USAGE_CHECK_ALL(Set##TYPENAME, SINGULAR, CPPTYPE); \ + if (field->is_extension()) { \ + return extensions_->Set##TYPENAME(field->number(), value); \ + } else { \ + SetField(field, value); \ + } \ + } \ + \ + PASSTYPE GeneratedMessageReflection::GetRepeated##TYPENAME( \ + const FieldDescriptor* field, int index) const { \ + USAGE_CHECK_ALL(GetRepeated##TYPENAME, REPEATED, CPPTYPE); \ + if (field->is_extension()) { \ + return extensions_->GetRepeated##TYPENAME(field->number(), index); \ + } else { \ + return GetRepeatedField(field, index); \ + } \ + } \ + \ + void GeneratedMessageReflection::SetRepeated##TYPENAME( \ + const FieldDescriptor* field, int index, PASSTYPE value) { \ + USAGE_CHECK_ALL(SetRepeated##TYPENAME, REPEATED, CPPTYPE); \ + if (field->is_extension()) { \ + extensions_->SetRepeated##TYPENAME(field->number(), index, value); \ + } else { \ + SetRepeatedField(field, index, value); \ + } \ + } \ + \ + void GeneratedMessageReflection::Add##TYPENAME( \ + const FieldDescriptor* field, PASSTYPE value) { \ + USAGE_CHECK_ALL(Add##TYPENAME, REPEATED, CPPTYPE); \ + if (field->is_extension()) { \ + extensions_->Add##TYPENAME(field->number(), value); \ + } else { \ + AddField(field, value); \ + } \ + } + +DEFINE_PRIMITIVE_ACCESSORS(Int32 , int32 , int32 , INT32 ) +DEFINE_PRIMITIVE_ACCESSORS(Int64 , int64 , int64 , INT64 ) +DEFINE_PRIMITIVE_ACCESSORS(UInt32, uint32, uint32, UINT32) +DEFINE_PRIMITIVE_ACCESSORS(UInt64, uint64, uint64, UINT64) +DEFINE_PRIMITIVE_ACCESSORS(Float , float , float , FLOAT ) +DEFINE_PRIMITIVE_ACCESSORS(Double, double, double, DOUBLE) +DEFINE_PRIMITIVE_ACCESSORS(Bool , bool , bool , BOOL ) +#undef DEFINE_PRIMITIVE_ACCESSORS + +// ------------------------------------------------------------------- + +string GeneratedMessageReflection::GetString( + const FieldDescriptor* field) const { + USAGE_CHECK_ALL(GetString, SINGULAR, STRING); + if (field->is_extension()) { + return extensions_->GetString(field->number()); + } else { + return *GetField(field); + } +} + +const string& GeneratedMessageReflection::GetStringReference( + const FieldDescriptor* field, string* scratch) const { + USAGE_CHECK_ALL(GetStringReference, SINGULAR, STRING); + if (field->is_extension()) { + return extensions_->GetString(field->number()); + } else { + return *GetField(field); + } +} + + +void GeneratedMessageReflection::SetString( + const FieldDescriptor* field, const string& value) { + USAGE_CHECK_ALL(SetString, SINGULAR, STRING); + if (field->is_extension()) { + return extensions_->SetString(field->number(), value); + } else { + string** ptr = MutableField(field); + if (*ptr == DefaultRaw(field)) { + *ptr = new string(value); + } else { + (*ptr)->assign(value); + } + } +} + + +string GeneratedMessageReflection::GetRepeatedString( + const FieldDescriptor* field, int index) const { + USAGE_CHECK_ALL(GetRepeatedString, REPEATED, STRING); + if (field->is_extension()) { + return extensions_->GetRepeatedString(field->number(), index); + } else { + return GetRepeatedField(field, index); + } +} + +const string& GeneratedMessageReflection::GetRepeatedStringReference( + const FieldDescriptor* field, int index, string* scratch) const { + USAGE_CHECK_ALL(GetRepeatedStringReference, REPEATED, STRING); + if (field->is_extension()) { + return extensions_->GetRepeatedString(field->number(), index); + } else { + return GetRepeatedField(field, index); + } +} + + +void GeneratedMessageReflection::SetRepeatedString( + const FieldDescriptor* field, int index, const string& value) { + USAGE_CHECK_ALL(SetRepeatedString, REPEATED, STRING); + if (field->is_extension()) { + extensions_->SetRepeatedString(field->number(), index, value); + } else { + SetRepeatedField(field, index, value); + } +} + + +void GeneratedMessageReflection::AddString( + const FieldDescriptor* field, const string& value) { + USAGE_CHECK_ALL(AddString, REPEATED, STRING); + if (field->is_extension()) { + extensions_->AddString(field->number(), value); + } else { + AddField(field, value); + } +} + + +// ------------------------------------------------------------------- + +const EnumValueDescriptor* GeneratedMessageReflection::GetEnum( + const FieldDescriptor* field) const { + USAGE_CHECK_ALL(GetEnum, SINGULAR, ENUM); + + int value; + if (field->is_extension()) { + value = extensions_->GetEnum(field->number()); + } else { + value = GetField(field); + } + const EnumValueDescriptor* result = + field->enum_type()->FindValueByNumber(value); + GOOGLE_CHECK(result != NULL); + return result; +} + +void GeneratedMessageReflection::SetEnum(const FieldDescriptor* field, + const EnumValueDescriptor* value) { + USAGE_CHECK_ALL(SetEnum, SINGULAR, ENUM); + USAGE_CHECK_ENUM_VALUE(SetEnum); + + if (field->is_extension()) { + extensions_->SetEnum(field->number(), value->number()); + } else { + SetField(field, value->number()); + } +} + +const EnumValueDescriptor* GeneratedMessageReflection::GetRepeatedEnum( + const FieldDescriptor* field, int index) const { + USAGE_CHECK_ALL(GetRepeatedEnum, REPEATED, ENUM); + + int value; + if (field->is_extension()) { + value = extensions_->GetRepeatedEnum(field->number(), index); + } else { + value = GetRepeatedField(field, index); + } + const EnumValueDescriptor* result = + field->enum_type()->FindValueByNumber(value); + GOOGLE_CHECK(result != NULL); + return result; +} + +void GeneratedMessageReflection::SetRepeatedEnum( + const FieldDescriptor* field, int index, + const EnumValueDescriptor* value) { + USAGE_CHECK_ALL(SetRepeatedEnum, REPEATED, ENUM); + USAGE_CHECK_ENUM_VALUE(SetRepeatedEnum); + + if (field->is_extension()) { + extensions_->SetRepeatedEnum(field->number(), index, value->number()); + } else { + SetRepeatedField(field, index, value->number()); + } +} + +void GeneratedMessageReflection::AddEnum(const FieldDescriptor* field, + const EnumValueDescriptor* value) { + USAGE_CHECK_ALL(AddEnum, REPEATED, ENUM); + USAGE_CHECK_ENUM_VALUE(AddEnum); + + if (field->is_extension()) { + extensions_->AddEnum(field->number(), value->number()); + } else { + AddField(field, value->number()); + } +} + +// ------------------------------------------------------------------- + +const Message& GeneratedMessageReflection::GetMessage( + const FieldDescriptor* field) const { + USAGE_CHECK_ALL(GetMessage, SINGULAR, MESSAGE); + + if (field->is_extension()) { + return extensions_->GetMessage(field->number()); + } else { + const Message* result = GetRaw(field); + if (result == NULL) { + result = DefaultRaw(field); + } + return *result; + } +} + +Message* GeneratedMessageReflection::MutableMessage( + const FieldDescriptor* field) { + USAGE_CHECK_ALL(MutableMessage, SINGULAR, MESSAGE); + + if (field->is_extension()) { + return extensions_->MutableMessage(field->number()); + } else { + Message** result = MutableField(field); + if (*result == NULL) { + const Message* default_message = DefaultRaw(field); + *result = default_message->New(); + (*result)->CopyFrom(*default_message); + } + return *result; + } +} + +const Message& GeneratedMessageReflection::GetRepeatedMessage( + const FieldDescriptor* field, int index) const { + USAGE_CHECK_ALL(GetRepeatedMessage, REPEATED, MESSAGE); + + if (field->is_extension()) { + return extensions_->GetRepeatedMessage(field->number(), index); + } else { + return GetRepeatedField(field, index); + } +} + +Message* GeneratedMessageReflection::MutableRepeatedMessage( + const FieldDescriptor* field, int index) { + USAGE_CHECK_ALL(MutableRepeatedMessage, REPEATED, MESSAGE); + + if (field->is_extension()) { + return extensions_->MutableRepeatedMessage(field->number(), index); + } else { + return MutableRepeatedField(field, index); + } +} + +Message* GeneratedMessageReflection::AddMessage(const FieldDescriptor* field) { + USAGE_CHECK_ALL(AddMessage, REPEATED, MESSAGE); + + if (field->is_extension()) { + return extensions_->AddMessage(field->number()); + } else { + return AddField(field); + } +} + +// ------------------------------------------------------------------- + +const FieldDescriptor* GeneratedMessageReflection::FindKnownExtensionByName( + const string& name) const { + if (extensions_ == NULL) return NULL; + return extensions_->FindKnownExtensionByName(name); +} + +const FieldDescriptor* GeneratedMessageReflection::FindKnownExtensionByNumber( + int number) const { + if (extensions_ == NULL) return NULL; + return extensions_->FindKnownExtensionByNumber(number); +} + +// =================================================================== +// Some private helpers. + +// These simple template accessors obtain pointers (or references) to +// the given field. +template +inline const Type& GeneratedMessageReflection::GetRaw( + const FieldDescriptor* field) const { + const void* ptr = reinterpret_cast(base_) + + offsets_[field->index()]; + return *reinterpret_cast(ptr); +} + +template +inline Type* GeneratedMessageReflection::MutableRaw( + const FieldDescriptor* field) { + void* ptr = reinterpret_cast(base_) + offsets_[field->index()]; + return reinterpret_cast(ptr); +} + +template +inline const Type& GeneratedMessageReflection::DefaultRaw( + const FieldDescriptor* field) const { + const void* ptr = reinterpret_cast(default_base_) + + offsets_[field->index()]; + return *reinterpret_cast(ptr); +} + +// Simple accessors for manipulating has_bits_. +inline bool GeneratedMessageReflection::HasBit( + const FieldDescriptor* field) const { + return has_bits_[field->index() / 32] & (1 << (field->index() % 32)); +} + +inline void GeneratedMessageReflection::SetBit( + const FieldDescriptor* field) { + has_bits_[field->index() / 32] |= (1 << (field->index() % 32)); +} + +inline void GeneratedMessageReflection::ClearBit( + const FieldDescriptor* field) { + has_bits_[field->index() / 32] &= ~(1 << (field->index() % 32)); +} + +// Template implementations of basic accessors. Inline because each +// template instance is only called from one location. These are +// used for all types except messages. +template +inline const Type& GeneratedMessageReflection::GetField( + const FieldDescriptor* field) const { + return GetRaw(field); +} + +template +inline void GeneratedMessageReflection::SetField( + const FieldDescriptor* field, const Type& value) { + *MutableRaw(field) = value; + SetBit(field); +} + +template +inline Type* GeneratedMessageReflection::MutableField( + const FieldDescriptor* field) { + SetBit(field); + return MutableRaw(field); +} + +template +inline const Type& GeneratedMessageReflection::GetRepeatedField( + const FieldDescriptor* field, int index) const { + return *reinterpret_cast( + GetRaw(field).GenericGet(index)); +} + +template +inline void GeneratedMessageReflection::SetRepeatedField( + const FieldDescriptor* field, int index, const Type& value) { + GenericRepeatedField* repeated = MutableRaw(field); + *reinterpret_cast(repeated->GenericMutable(index)) = value; +} + +template +inline Type* GeneratedMessageReflection::MutableRepeatedField( + const FieldDescriptor* field, int index) { + GenericRepeatedField* repeated = MutableRaw(field); + return reinterpret_cast(repeated->GenericMutable(index)); +} + +template +inline void GeneratedMessageReflection::AddField( + const FieldDescriptor* field, const Type& value) { + GenericRepeatedField* repeated = MutableRaw(field); + *reinterpret_cast(repeated->GenericAdd()) = value; +} + +template +inline Type* GeneratedMessageReflection::AddField( + const FieldDescriptor* field) { + GenericRepeatedField* repeated = MutableRaw(field); + return reinterpret_cast(repeated->GenericAdd()); +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h new file mode 100644 index 00000000..579d6abe --- /dev/null +++ b/src/google/protobuf/generated_message_reflection.h @@ -0,0 +1,300 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__ +#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__ + +#include +#include +#include +#include + + +// Generated code needs this to have been forward-declared. Easier to do it +// here than to print it inside every .pb.h file. +namespace google { +namespace protobuf { class EnumDescriptor; } + +namespace protobuf { +namespace internal { + +// Defined in this file. +class GeneratedMessageReflection; + +// Defined in other files. +class ExtensionSet; // extension_set.h + +// THIS CLASS IS NOT INTENDED FOR DIRECT USE. It is intended for use +// by generated code. This class is just a big hack that reduces code +// size. +// +// A GeneratedMessageReflection is an implementation of Message::Reflection +// which expects all fields to be backed by simple variables located in +// memory. The locations are given using a base pointer and a set of +// offsets. +// +// It is required that the user represents fields of each type in a standard +// way, so that GeneratedMessageReflection can cast the void* pointer to +// the appropriate type. For primitive fields and string fields, each field +// should be represented using the obvious C++ primitive type. Enums and +// Messages are different: +// - Singular Message fields are stored as a pointer to a Message. These +// should start out NULL, except for in the default instance where they +// should start out pointing to other default instances. +// - Enum fields are stored as an int. This int must always contain +// a valid value, such that EnumDescriptor::FindValueByNumber() would +// not return NULL. +// - Repeated fields are stored as RepeatedFields or RepeatedPtrFields +// of whatever type the individual field would be. Strings and +// Messages use RepeatedPtrFields while everything else uses +// RepeatedFields. +class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Message::Reflection { + public: + // Constructs a GeneratedMessageReflection. + // Parameters: + // descriptor: The descriptor for the message type being implemented. + // base: Pointer to the location where the message object is + // stored. + // default_base: Pointer to the location where the message's default + // instance is stored. This is only used to obtain + // pointers to default instances of embedded messages, + // which GetMessage() will return if the particular sub- + // message has not been initialized yet. (Thus, all + // embedded message fields *must* have non-NULL pointers + // in the default instance.) + // offsets: An array of bits giving the byte offsets, relative to + // "base" and "default_base", of each field. These can + // be computed at compile time using the + // GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET() macro, defined + // below. + // has_bits: An array of uint32s of size descriptor->field_count()/32, + // rounded up. This is a bitfield where each bit indicates + // whether or not the corresponding field of the message + // has been initialized. The bit for field index i is + // obtained by the expression: + // has_bits[i / 32] & (1 << (i % 32)) + // extensions: The ExtensionSet for this message, or NULL if the + // message type has no extension ranges. + GeneratedMessageReflection(const Descriptor* descriptor, + void* base, const void* default_base, + const int offsets[], uint32 has_bits[], + ExtensionSet* extensions); + ~GeneratedMessageReflection(); + + inline const UnknownFieldSet& unknown_fields() const { + return unknown_fields_; + } + inline UnknownFieldSet* mutable_unknown_fields() { + return &unknown_fields_; + } + + // implements Message::Reflection ---------------------------------- + + const UnknownFieldSet& GetUnknownFields() const; + UnknownFieldSet* MutableUnknownFields(); + + bool HasField(const FieldDescriptor* field) const; + int FieldSize(const FieldDescriptor* field) const; + void ClearField(const FieldDescriptor* field); + void ListFields(vector* output) const; + + int32 GetInt32 (const FieldDescriptor* field) const; + int64 GetInt64 (const FieldDescriptor* field) const; + uint32 GetUInt32(const FieldDescriptor* field) const; + uint64 GetUInt64(const FieldDescriptor* field) const; + float GetFloat (const FieldDescriptor* field) const; + double GetDouble(const FieldDescriptor* field) const; + bool GetBool (const FieldDescriptor* field) const; + string GetString(const FieldDescriptor* field) const; + const string& GetStringReference(const FieldDescriptor* field, + string* scratch) const; + const EnumValueDescriptor* GetEnum(const FieldDescriptor* field) const; + const Message& GetMessage(const FieldDescriptor* field) const; + + void SetInt32 (const FieldDescriptor* field, int32 value); + void SetInt64 (const FieldDescriptor* field, int64 value); + void SetUInt32(const FieldDescriptor* field, uint32 value); + void SetUInt64(const FieldDescriptor* field, uint64 value); + void SetFloat (const FieldDescriptor* field, float value); + void SetDouble(const FieldDescriptor* field, double value); + void SetBool (const FieldDescriptor* field, bool value); + void SetString(const FieldDescriptor* field, + const string& value); + void SetEnum (const FieldDescriptor* field, + const EnumValueDescriptor* value); + Message* MutableMessage(const FieldDescriptor* field); + + int32 GetRepeatedInt32 (const FieldDescriptor* field, int index) const; + int64 GetRepeatedInt64 (const FieldDescriptor* field, int index) const; + uint32 GetRepeatedUInt32(const FieldDescriptor* field, int index) const; + uint64 GetRepeatedUInt64(const FieldDescriptor* field, int index) const; + float GetRepeatedFloat (const FieldDescriptor* field, int index) const; + double GetRepeatedDouble(const FieldDescriptor* field, int index) const; + bool GetRepeatedBool (const FieldDescriptor* field, int index) const; + string GetRepeatedString(const FieldDescriptor* field, int index) const; + const string& GetRepeatedStringReference(const FieldDescriptor* field, + int index, string* scratch) const; + const EnumValueDescriptor* GetRepeatedEnum(const FieldDescriptor* field, + int index) const; + const Message& GetRepeatedMessage(const FieldDescriptor* field, + int index) const; + + // Set the value of a field. + void SetRepeatedInt32 (const FieldDescriptor* field, int index, int32 value); + void SetRepeatedInt64 (const FieldDescriptor* field, int index, int64 value); + void SetRepeatedUInt32(const FieldDescriptor* field, int index, uint32 value); + void SetRepeatedUInt64(const FieldDescriptor* field, int index, uint64 value); + void SetRepeatedFloat (const FieldDescriptor* field, int index, float value); + void SetRepeatedDouble(const FieldDescriptor* field, int index, double value); + void SetRepeatedBool (const FieldDescriptor* field, int index, bool value); + void SetRepeatedString(const FieldDescriptor* field, int index, + const string& value); + void SetRepeatedEnum (const FieldDescriptor* field, int index, + const EnumValueDescriptor* value); + // Get a mutable pointer to a field with a message type. + Message* MutableRepeatedMessage(const FieldDescriptor* field, int index); + + void AddInt32 (const FieldDescriptor* field, int32 value); + void AddInt64 (const FieldDescriptor* field, int64 value); + void AddUInt32(const FieldDescriptor* field, uint32 value); + void AddUInt64(const FieldDescriptor* field, uint64 value); + void AddFloat (const FieldDescriptor* field, float value); + void AddDouble(const FieldDescriptor* field, double value); + void AddBool (const FieldDescriptor* field, bool value); + void AddString(const FieldDescriptor* field, const string& value); + void AddEnum(const FieldDescriptor* field, const EnumValueDescriptor* value); + Message* AddMessage(const FieldDescriptor* field); + + const FieldDescriptor* FindKnownExtensionByName(const string& name) const; + const FieldDescriptor* FindKnownExtensionByNumber(int number) const; + + private: + friend class GeneratedMessage; + + const Descriptor* descriptor_; + void* base_; + const void* default_base_; + const int* offsets_; + + // TODO(kenton): These two pointers just point back into the message object. + // We could save space by removing them and using offsets instead. + uint32* has_bits_; + ExtensionSet* extensions_; + + // We put this directly in the GeneratedMessageReflection because every + // message class needs it, and if we don't find any unknown fields, it + // takes up only one pointer of space. + UnknownFieldSet unknown_fields_; + + template + inline const Type& GetRaw(const FieldDescriptor* field) const; + template + inline Type* MutableRaw(const FieldDescriptor* field); + template + inline const Type& DefaultRaw(const FieldDescriptor* field) const; + inline const Message* GetMessagePrototype(const FieldDescriptor* field) const; + + inline bool HasBit(const FieldDescriptor* field) const; + inline void SetBit(const FieldDescriptor* field); + inline void ClearBit(const FieldDescriptor* field); + + template + inline const Type& GetField(const FieldDescriptor* field) const; + template + inline void SetField(const FieldDescriptor* field, const Type& value); + template + inline Type* MutableField(const FieldDescriptor* field); + template + inline const Type& GetRepeatedField(const FieldDescriptor* field, + int index) const; + template + inline void SetRepeatedField(const FieldDescriptor* field, int index, + const Type& value); + template + inline Type* MutableRepeatedField(const FieldDescriptor* field, int index); + template + inline void AddField(const FieldDescriptor* field, const Type& value); + template + inline Type* AddField(const FieldDescriptor* field); + + int GetExtensionNumberOrDie(const Descriptor* type) const; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratedMessageReflection); +}; + +// Returns the offset of the given field within the given aggregate type. +// This is equivalent to the ANSI C offsetof() macro. However, according +// to the C++ standard, offsetof() only works on POD types, and GCC +// enforces this requirement with a warning. In practice, this rule is +// unnecessarily strict; there is probably no compiler or platform on +// which the offsets of the direct fields of a class are non-constant. +// Fields inherited from superclasses *can* have non-constant offsets, +// but that's not what this macro will be used for. +// +// Note that we calculate relative to the pointer value 16 here since if we +// just use zero, GCC complains about dereferencing a NULL pointer. We +// choose 16 rather than some other number just in case the compiler would +// be confused by an unaligned pointer. +#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \ + (reinterpret_cast( \ + &reinterpret_cast(16)->FIELD) - \ + reinterpret_cast(16)) + +// There are some places in proto2 where dynamic_cast would be useful as an +// optimization. For example, take Message::MergeFrom(const Message& other). +// For a given generated message FooMessage, we generate these two methods: +// void MergeFrom(const FooMessage& other); +// void MergeFrom(const Message& other); +// The former method can be implemented directly in terms of FooMessage's +// inline accessors, but the latter method must work with the reflection +// interface. However, if the parameter to the latter method is actually of +// type FooMessage, then we'd like to be able to just call the other method +// as an optimization. So, we use dynamic_cast to check this. +// +// That said, dynamic_cast requires RTTI, which many people like to disable +// for performance and code size reasons. When RTTI is not available, we +// still need to produce correct results. So, in this case we have to fall +// back to using reflection, which is what we would have done anyway if the +// objects were not of the exact same class. +// +// dynamic_cast_if_available() implements this logic. If RTTI is +// enabled, it does a dynamic_cast. If RTTI is disabled, it just returns +// NULL. +// +// If you need to compile without RTTI, simply #define GOOGLE_PROTOBUF_NO_RTTI. +// On MSVC, this should be detected automatically. +template +inline To dynamic_cast_if_available(From from) { +#if defined(GOOGLE_PROTOBUF_NO_RTTI) || (defined(_MSC_VER)&&!defined(_CPPRTTI)) + return NULL; +#else + return dynamic_cast(from); +#endif +} + + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__ diff --git a/src/google/protobuf/generated_message_reflection_unittest.cc b/src/google/protobuf/generated_message_reflection_unittest.cc new file mode 100644 index 00000000..bde7fac7 --- /dev/null +++ b/src/google/protobuf/generated_message_reflection_unittest.cc @@ -0,0 +1,251 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// To test GeneratedMessageReflection, we actually let the protocol compiler +// generate a full protocol message implementation and then test its +// reflection interface. This is much easier and more maintainable than +// trying to create our own Message class for GeneratedMessageReflection +// to wrap. +// +// The tests here closely mirror some of the tests in +// compiler/cpp/unittest, except using the reflection interface +// rather than generated accessors. + +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace { + +// Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes. +const FieldDescriptor* F(const string& name) { + const FieldDescriptor* result = + unittest::TestAllTypes::descriptor()->FindFieldByName(name); + GOOGLE_CHECK(result != NULL); + return result; +} + +TEST(GeneratedMessageReflectionTest, Defaults) { + // Check that all default values are set correctly in the initial message. + unittest::TestAllTypes message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + + reflection_tester.ExpectClearViaReflection(*message.GetReflection()); + + const Message::Reflection& reflection = *message.GetReflection(); + + // Messages should return pointers to default instances until first use. + // (This is not checked by ExpectClear() since it is not actually true after + // the fields have been set and then cleared.) + EXPECT_EQ(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &reflection.GetMessage(F("optionalgroup"))); + EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(), + &reflection.GetMessage(F("optional_nested_message"))); + EXPECT_EQ(&unittest::ForeignMessage::default_instance(), + &reflection.GetMessage(F("optional_foreign_message"))); + EXPECT_EQ(&unittest_import::ImportMessage::default_instance(), + &reflection.GetMessage(F("optional_import_message"))); +} + +TEST(GeneratedMessageReflectionTest, Accessors) { + // Set every field to a unique value then go back and check all those + // values. + unittest::TestAllTypes message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + + reflection_tester.SetAllFieldsViaReflection(message.GetReflection()); + TestUtil::ExpectAllFieldsSet(message); + reflection_tester.ExpectAllFieldsSetViaReflection(*message.GetReflection()); + + reflection_tester.ModifyRepeatedFieldsViaReflection(message.GetReflection()); + TestUtil::ExpectRepeatedFieldsModified(message); +} + +TEST(GeneratedMessageReflectionTest, GetStringReference) { + // Test that GetStringReference() returns the underlying string when it is + // a normal string field. + unittest::TestAllTypes message; + message.set_optional_string("foo"); + message.add_repeated_string("foo"); + + const Message::Reflection& reflection = *message.GetReflection(); + string scratch; + + EXPECT_EQ(&message.optional_string(), + &reflection.GetStringReference(F("optional_string"), &scratch)) + << "For simple string fields, GetStringReference() should return a " + "reference to the underlying string."; + EXPECT_EQ(&message.repeated_string(0), + &reflection.GetRepeatedStringReference(F("repeated_string"), 0, &scratch)) + << "For simple string fields, GetRepeatedStringReference() should return " + "a reference to the underlying string."; +} + + +TEST(GeneratedMessageReflectionTest, DefaultsAfterClear) { + // Check that after setting all fields and then clearing, getting an + // embedded message does NOT return the default instance. + unittest::TestAllTypes message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + + TestUtil::SetAllFields(&message); + message.Clear(); + + const Message::Reflection& reflection = *message.GetReflection(); + + EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &reflection.GetMessage(F("optionalgroup"))); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &reflection.GetMessage(F("optional_nested_message"))); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &reflection.GetMessage(F("optional_foreign_message"))); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &reflection.GetMessage(F("optional_import_message"))); +} + +TEST(GeneratedMessageReflectionTest, Extensions) { + // Set every extension to a unique value then go back and check all those + // values. + unittest::TestAllExtensions message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllExtensions::descriptor()); + + reflection_tester.SetAllFieldsViaReflection(message.GetReflection()); + TestUtil::ExpectAllExtensionsSet(message); + reflection_tester.ExpectAllFieldsSetViaReflection(*message.GetReflection()); + + reflection_tester.ModifyRepeatedFieldsViaReflection(message.GetReflection()); + TestUtil::ExpectRepeatedExtensionsModified(message); +} + +TEST(GeneratedMessageReflectionTest, FindExtensionTypeByNumber) { + const Message::Reflection& reflection = + *unittest::TestAllExtensions::default_instance().GetReflection(); + + const FieldDescriptor* extension1 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "optional_int32_extension"); + const FieldDescriptor* extension2 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "repeated_string_extension"); + + EXPECT_EQ(extension1, + reflection.FindKnownExtensionByNumber(extension1->number())); + EXPECT_EQ(extension2, + reflection.FindKnownExtensionByNumber(extension2->number())); + + // Non-existent extension. + EXPECT_TRUE(reflection.FindKnownExtensionByNumber(62341) == NULL); + + // Extensions of TestAllExtensions should not show up as extensions of + // other types. + EXPECT_TRUE(unittest::TestAllTypes::default_instance().GetReflection()-> + FindKnownExtensionByNumber(extension1->number()) == NULL); +} + +TEST(GeneratedMessageReflectionTest, FindKnownExtensionByName) { + const Message::Reflection& reflection = + *unittest::TestAllExtensions::default_instance().GetReflection(); + + const FieldDescriptor* extension1 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "optional_int32_extension"); + const FieldDescriptor* extension2 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "repeated_string_extension"); + + EXPECT_EQ(extension1, + reflection.FindKnownExtensionByName(extension1->full_name())); + EXPECT_EQ(extension2, + reflection.FindKnownExtensionByName(extension2->full_name())); + + // Non-existent extension. + EXPECT_TRUE(reflection.FindKnownExtensionByName("no_such_ext") == NULL); + + // Extensions of TestAllExtensions should not show up as extensions of + // other types. + EXPECT_TRUE(unittest::TestAllTypes::default_instance().GetReflection()-> + FindKnownExtensionByName(extension1->full_name()) == NULL); +} + +#ifdef GTEST_HAS_DEATH_TEST + +TEST(GeneratedMessageReflectionTest, UsageErrors) { + unittest::TestAllTypes message; + Message::Reflection* reflection = message.GetReflection(); + const Descriptor* descriptor = message.GetDescriptor(); + +#define f(NAME) descriptor->FindFieldByName(NAME) + + // Testing every single failure mode would be too much work. Let's just + // check a few. + EXPECT_DEATH( + reflection->GetInt32(descriptor->FindFieldByName("optional_int64")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Message::Reflection::GetInt32\n" + " Message type: protobuf_unittest\\.TestAllTypes\n" + " Field : protobuf_unittest\\.TestAllTypes\\.optional_int64\n" + " Problem : Field is not the right type for this message:\n" + " Expected : CPPTYPE_INT32\n" + " Field type: CPPTYPE_INT64"); + EXPECT_DEATH( + reflection->GetInt32(descriptor->FindFieldByName("repeated_int32")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Message::Reflection::GetInt32\n" + " Message type: protobuf_unittest.TestAllTypes\n" + " Field : protobuf_unittest.TestAllTypes.repeated_int32\n" + " Problem : Field is repeated; the method requires a singular field."); + EXPECT_DEATH( + reflection->GetInt32( + unittest::ForeignMessage::descriptor()->FindFieldByName("c")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Message::Reflection::GetInt32\n" + " Message type: protobuf_unittest.TestAllTypes\n" + " Field : protobuf_unittest.ForeignMessage.c\n" + " Problem : Field does not match message type."); + EXPECT_DEATH( + reflection->HasField( + unittest::ForeignMessage::descriptor()->FindFieldByName("c")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Message::Reflection::HasField\n" + " Message type: protobuf_unittest.TestAllTypes\n" + " Field : protobuf_unittest.ForeignMessage.c\n" + " Problem : Field does not match message type."); + +#undef f +} + +#endif // GTEST_HAS_DEATH_TEST + + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc new file mode 100644 index 00000000..58c44dc1 --- /dev/null +++ b/src/google/protobuf/io/coded_stream.cc @@ -0,0 +1,757 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This implementation is heavily optimized to make reads and writes +// of small values (especially varints) as fast as possible. In +// particular, we optimize for the common case that a read or a write +// will not cross the end of the buffer, since we can avoid a lot +// of branching in this case. + +#include +#include +#include +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace io { + +namespace { + +static const int kDefaultTotalBytesLimit = 64 << 20; // 64MB + +static const int kDefaultTotalBytesWarningThreshold = 32 << 20; // 32MB +static const int kDefaultRecursionLimit = 64; + +static const int kMaxVarintBytes = 10; +static const int kMaxVarint32Bytes = 5; + + +} // namespace + +// CodedInputStream ================================================== + +CodedInputStream::CodedInputStream(ZeroCopyInputStream* input) + : input_(input), + buffer_(NULL), + buffer_size_(0), + total_bytes_read_(0), + overflow_bytes_(0), + + last_tag_(0), + legitimate_message_end_(false), + + aliasing_enabled_(false), + + current_limit_(INT_MAX), + buffer_size_after_limit_(0), + + total_bytes_limit_(kDefaultTotalBytesLimit), + total_bytes_warning_threshold_(kDefaultTotalBytesWarningThreshold), + + recursion_depth_(0), + recursion_limit_(kDefaultRecursionLimit) { +} + +CodedInputStream::~CodedInputStream() { + int backup_bytes = buffer_size_ + buffer_size_after_limit_ + overflow_bytes_; + if (backup_bytes > 0) { + // We still have bytes left over from the last buffer. Back up over + // them. + input_->BackUp(backup_bytes); + } +} + + +inline void CodedInputStream::RecomputeBufferLimits() { + buffer_size_ += buffer_size_after_limit_; + int closest_limit = min(current_limit_, total_bytes_limit_); + if (closest_limit < total_bytes_read_) { + // The limit position is in the current buffer. We must adjust + // the buffer size accordingly. + buffer_size_after_limit_ = total_bytes_read_ - closest_limit; + buffer_size_ -= buffer_size_after_limit_; + } else { + buffer_size_after_limit_ = 0; + } +} + +CodedInputStream::Limit CodedInputStream::PushLimit(int byte_limit) { + // Current position relative to the beginning of the stream. + int current_position = total_bytes_read_ - + (buffer_size_ + buffer_size_after_limit_); + + Limit old_limit = current_limit_; + + // security: byte_limit is possibly evil, so check for negative values + // and overflow. + if (byte_limit >= 0 && + byte_limit <= INT_MAX - current_position) { + current_limit_ = current_position + byte_limit; + } else { + // Negative or overflow. + current_limit_ = INT_MAX; + } + + // We need to enforce all limits, not just the new one, so if the previous + // limit was before the new requested limit, we continue to enforce the + // previous limit. + current_limit_ = min(current_limit_, old_limit); + + RecomputeBufferLimits(); + return old_limit; +} + +void CodedInputStream::PopLimit(Limit limit) { + // The limit passed in is actually the *old* limit, which we returned from + // PushLimit(). + current_limit_ = limit; + RecomputeBufferLimits(); + + // We may no longer be at a legitimate message end. ReadTag() needs to be + // called again to find out. + legitimate_message_end_ = false; +} + +int CodedInputStream::BytesUntilLimit() { + if (current_limit_ == INT_MAX) return -1; + int current_position = total_bytes_read_ - + (buffer_size_ + buffer_size_after_limit_); + + return current_limit_ - current_position; +} + +void CodedInputStream::SetTotalBytesLimit( + int total_bytes_limit, int warning_threshold) { + // Make sure the limit isn't already past, since this could confuse other + // code. + int current_position = total_bytes_read_ - + (buffer_size_ + buffer_size_after_limit_); + total_bytes_limit_ = max(current_position, total_bytes_limit); + total_bytes_warning_threshold_ = warning_threshold; + RecomputeBufferLimits(); +} + +void CodedInputStream::PrintTotalBytesLimitError() { + GOOGLE_LOG(ERROR) << "A protocol message was rejected because it was too " + "big (more than " << total_bytes_limit_ + << " bytes). To increase the limit (or to disable these " + "warnings), see CodedInputStream::SetTotalBytesLimit() " + "in google/protobuf/io/coded_stream.h."; +} + +bool CodedInputStream::Skip(int count) { + if (count < 0) return false; // security: count is often user-supplied + + if (count <= buffer_size_) { + // Just skipping within the current buffer. Easy. + Advance(count); + return true; + } + + if (buffer_size_after_limit_ > 0) { + // We hit a limit inside this buffer. Advance to the limit and fail. + Advance(buffer_size_); + return false; + } + + count -= buffer_size_; + buffer_ = NULL; + buffer_size_ = 0; + + // Make sure this skip doesn't try to skip past the current limit. + int closest_limit = min(current_limit_, total_bytes_limit_); + int bytes_until_limit = closest_limit - total_bytes_read_; + if (bytes_until_limit < count) { + // We hit the limit. Skip up to it then fail. + total_bytes_read_ = closest_limit; + input_->Skip(bytes_until_limit); + return false; + } + + total_bytes_read_ += count; + return input_->Skip(count); +} + +bool CodedInputStream::ReadRaw(void* buffer, int size) { + while (buffer_size_ < size) { + // Reading past end of buffer. Copy what we have, then refresh. + memcpy(buffer, buffer_, buffer_size_); + buffer = reinterpret_cast(buffer) + buffer_size_; + size -= buffer_size_; + if (!Refresh()) return false; + } + + memcpy(buffer, buffer_, size); + Advance(size); + + return true; +} + +bool CodedInputStream::ReadString(string* buffer, int size) { + if (size < 0) return false; // security: size is often user-supplied + + if (!buffer->empty()) { + buffer->clear(); + } + + if (size < buffer_size_) { + STLStringResizeUninitialized(buffer, size); + memcpy((uint8*)buffer->data(), buffer_, size); + Advance(size); + return true; + } + + while (buffer_size_ < size) { + // Some STL implementations "helpfully" crash on buffer->append(NULL, 0). + if (buffer_size_ != 0) { + // Note: string1.append(string2) is O(string2.size()) (as opposed to + // O(string1.size() + string2.size()), which would be bad). + buffer->append(reinterpret_cast(buffer_), buffer_size_); + } + size -= buffer_size_; + if (!Refresh()) return false; + } + + buffer->append(reinterpret_cast(buffer_), size); + Advance(size); + + return true; +} + + +bool CodedInputStream::ReadLittleEndian32(uint32* value) { + uint8 bytes[sizeof(*value)]; + + const uint8* ptr; + if (buffer_size_ >= sizeof(*value)) { + // Fast path: Enough bytes in the buffer to read directly. + ptr = buffer_; + Advance(sizeof(*value)); + } else { + // Slow path: Had to read past the end of the buffer. + if (!ReadRaw(bytes, sizeof(*value))) return false; + ptr = bytes; + } + + *value = (static_cast(ptr[0]) ) | + (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | + (static_cast(ptr[3]) << 24); + return true; +} + +bool CodedInputStream::ReadLittleEndian64(uint64* value) { + uint8 bytes[sizeof(*value)]; + + const uint8* ptr; + if (buffer_size_ >= sizeof(*value)) { + // Fast path: Enough bytes in the buffer to read directly. + ptr = buffer_; + Advance(sizeof(*value)); + } else { + // Slow path: Had to read past the end of the buffer. + if (!ReadRaw(bytes, sizeof(*value))) return false; + ptr = bytes; + } + + uint32 part0 = (static_cast(ptr[0]) ) | + (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | + (static_cast(ptr[3]) << 24); + uint32 part1 = (static_cast(ptr[4]) ) | + (static_cast(ptr[5]) << 8) | + (static_cast(ptr[6]) << 16) | + (static_cast(ptr[7]) << 24); + *value = static_cast(part0) | + (static_cast(part1) << 32); + return true; +} + +bool CodedInputStream::ReadVarint32Fallback(uint32* value) { + if (buffer_size_ >= kMaxVarintBytes || + // Optimization: If the varint ends at exactly the end of the buffer, + // we can detect that and still use the fast path. + (buffer_size_ != 0 && !(buffer_[buffer_size_-1] & 0x80))) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this read won't cross the end, so we can skip the checks. + const uint8* ptr = buffer_; + uint32 b; + uint32 result; + + b = *(ptr++); result = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= b << 28; if (!(b & 0x80)) goto done; + + // If the input is larger than 32 bits, we still need to read it all + // and discard the high-order bits. + for (int i = 0; i < kMaxVarintBytes - kMaxVarint32Bytes; i++) { + b = *(ptr++); if (!(b & 0x80)) goto done; + } + + // We have overrun the maximum size of a varint (10 bytes). Assume + // the data is corrupt. + return false; + + done: + Advance(ptr - buffer_); + *value = result; + return true; + + } else { + // Optimization: If we're at a limit, detect that quickly. (This is + // common when reading tags.) + while (buffer_size_ == 0) { + // Detect cases where we definitely hit a byte limit without calling + // Refresh(). + if (// If we hit a limit, buffer_size_after_limit_ will be non-zero. + buffer_size_after_limit_ > 0 && + // Make sure that the limit we hit is not total_bytes_limit_, since + // in that case we still need to call Refresh() so that it prints an + // error. + total_bytes_read_ - buffer_size_after_limit_ < total_bytes_limit_) { + // We hit a byte limit. + legitimate_message_end_ = true; + return false; + } + + // Call refresh. + if (!Refresh()) { + // Refresh failed. Make sure that it failed due to EOF, not because + // we hit total_bytes_limit_, which, unlike normal limits, is not a + // valid place to end a message. + int current_position = total_bytes_read_ - buffer_size_after_limit_; + if (current_position >= total_bytes_limit_) { + // Hit total_bytes_limit_. But if we also hit the normal limit, + // we're still OK. + legitimate_message_end_ = current_limit_ == total_bytes_limit_; + } else { + legitimate_message_end_ = true; + } + return false; + } + } + + // Slow path: Just do a 64-bit read. + uint64 result; + if (!ReadVarint64(&result)) return false; + *value = (uint32)result; + return true; + } +} + +bool CodedInputStream::ReadVarint64(uint64* value) { + if (buffer_size_ >= kMaxVarintBytes || + // Optimization: If the varint ends at exactly the end of the buffer, + // we can detect that and still use the fast path. + (buffer_size_ != 0 && !(buffer_[buffer_size_-1] & 0x80))) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this read won't cross the end, so we can skip the checks. + + const uint8* ptr = buffer_; + uint32 b; + + // Splitting into 32-bit pieces gives better performance on 32-bit + // processors. + uint32 part0 = 0, part1 = 0, part2 = 0; + + b = *(ptr++); part0 = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); part0 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + b = *(ptr++); part0 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; + b = *(ptr++); part0 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; + b = *(ptr++); part2 = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); part2 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + + // We have overrun the maximum size of a varint (10 bytes). The data + // must be corrupt. + return false; + + done: + Advance(ptr - buffer_); + *value = (static_cast(part0) ) | + (static_cast(part1) << 28) | + (static_cast(part2) << 56); + return true; + + } else { + // Slow path: This read might cross the end of the buffer, so we + // need to check and refresh the buffer if and when it does. + + uint64 result = 0; + int count = 0; + uint32 b; + + do { + if (count == kMaxVarintBytes) return false; + while (buffer_size_ == 0) { + if (!Refresh()) return false; + } + b = *buffer_; + result |= static_cast(b & 0x7F) << (7 * count); + Advance(1); + ++count; + } while(b & 0x80); + + *value = result; + return true; + } +} + +bool CodedInputStream::Refresh() { + if (buffer_size_after_limit_ > 0 || overflow_bytes_ > 0) { + // We've hit a limit. Stop. + buffer_ += buffer_size_; + buffer_size_ = 0; + + int current_position = total_bytes_read_ - buffer_size_after_limit_; + + if (current_position >= total_bytes_limit_ && + total_bytes_limit_ != current_limit_) { + // Hit total_bytes_limit_. + PrintTotalBytesLimitError(); + } + + return false; + } + + if (total_bytes_warning_threshold_ >= 0 && + total_bytes_read_ >= total_bytes_warning_threshold_) { + GOOGLE_LOG(WARNING) << "Reading dangerously large protocol message. If the " + "message turns out to be larger than " + << total_bytes_limit_ << " bytes, parsing will be halted " + "for security reasons. To increase the limit (or to " + "disable these warnings), see " + "CodedInputStream::SetTotalBytesLimit() in " + "google/protobuf/io/coded_stream.h."; + + // Don't warn again for this stream. + total_bytes_warning_threshold_ = -1; + } + + const void* void_buffer; + if (input_->Next(&void_buffer, &buffer_size_)) { + buffer_ = reinterpret_cast(void_buffer); + GOOGLE_CHECK_GE(buffer_size_, 0); + + if (total_bytes_read_ <= INT_MAX - buffer_size_) { + total_bytes_read_ += buffer_size_; + } else { + // Overflow. Reset buffer_size_ to not include the bytes beyond INT_MAX. + // We can't get that far anyway, because total_bytes_limit_ is guaranteed + // to be less than it. We need to keep track of the number of bytes + // we discarded, though, so that we can call input_->BackUp() to back + // up over them on destruction. + + // The following line is equivalent to: + // overflow_bytes_ = total_bytes_read_ + buffer_size_ - INT_MAX; + // except that it avoids overflows. Signed integer overflow has + // undefined results according to the C standard. + overflow_bytes_ = total_bytes_read_ - (INT_MAX - buffer_size_); + buffer_size_ -= overflow_bytes_; + total_bytes_read_ = INT_MAX; + } + + RecomputeBufferLimits(); + return true; + } else { + buffer_ = NULL; + buffer_size_ = 0; + return false; + } +} + +// CodedOutputStream ================================================= + +CodedOutputStream::CodedOutputStream(ZeroCopyOutputStream* output) + : output_(output), + buffer_(NULL), + buffer_size_(0), + total_bytes_(0) { +} + +CodedOutputStream::~CodedOutputStream() { + if (buffer_size_ > 0) { + output_->BackUp(buffer_size_); + } +} + +bool CodedOutputStream::WriteRaw(const void* data, int size) { + while (buffer_size_ < size) { + memcpy(buffer_, data, buffer_size_); + size -= buffer_size_; + data = reinterpret_cast(data) + buffer_size_; + if (!Refresh()) return false; + } + + memcpy(buffer_, data, size); + Advance(size); + return true; +} + + +bool CodedOutputStream::WriteLittleEndian32(uint32 value) { + uint8 bytes[sizeof(value)]; + + bool use_fast = buffer_size_ >= sizeof(value); + uint8* ptr = use_fast ? buffer_ : bytes; + + ptr[0] = static_cast(value ); + ptr[1] = static_cast(value >> 8); + ptr[2] = static_cast(value >> 16); + ptr[3] = static_cast(value >> 24); + + if (use_fast) { + Advance(sizeof(value)); + return true; + } else { + return WriteRaw(bytes, sizeof(value)); + } +} + +bool CodedOutputStream::WriteLittleEndian64(uint64 value) { + uint8 bytes[sizeof(value)]; + + uint32 part0 = static_cast(value); + uint32 part1 = static_cast(value >> 32); + + bool use_fast = buffer_size_ >= sizeof(value); + uint8* ptr = use_fast ? buffer_ : bytes; + + ptr[0] = static_cast(part0 ); + ptr[1] = static_cast(part0 >> 8); + ptr[2] = static_cast(part0 >> 16); + ptr[3] = static_cast(part0 >> 24); + ptr[4] = static_cast(part1 ); + ptr[5] = static_cast(part1 >> 8); + ptr[6] = static_cast(part1 >> 16); + ptr[7] = static_cast(part1 >> 24); + + if (use_fast) { + Advance(sizeof(value)); + return true; + } else { + return WriteRaw(bytes, sizeof(value)); + } +} + +bool CodedOutputStream::WriteVarint32Fallback(uint32 value) { + if (buffer_size_ >= kMaxVarint32Bytes) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this write won't cross the end, so we can skip the checks. + uint8* target = buffer_; + + target[0] = static_cast(value | 0x80); + if (value >= (1 << 7)) { + target[1] = static_cast((value >> 7) | 0x80); + if (value >= (1 << 14)) { + target[2] = static_cast((value >> 14) | 0x80); + if (value >= (1 << 21)) { + target[3] = static_cast((value >> 21) | 0x80); + if (value >= (1 << 28)) { + target[4] = static_cast(value >> 28); + Advance(5); + } else { + target[3] &= 0x7F; + Advance(4); + } + } else { + target[2] &= 0x7F; + Advance(3); + } + } else { + target[1] &= 0x7F; + Advance(2); + } + } else { + target[0] &= 0x7F; + Advance(1); + } + + return true; + } else { + // Slow path: This write might cross the end of the buffer, so we + // compose the bytes first then use WriteRaw(). + uint8 bytes[kMaxVarint32Bytes]; + int size = 0; + while (value > 0x7F) { + bytes[size++] = (static_cast(value) & 0x7F) | 0x80; + value >>= 7; + } + bytes[size++] = static_cast(value) & 0x7F; + return WriteRaw(bytes, size); + } +} + +bool CodedOutputStream::WriteVarint64(uint64 value) { + if (buffer_size_ >= kMaxVarintBytes) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this write won't cross the end, so we can skip the checks. + uint8* target = buffer_; + + // Splitting into 32-bit pieces gives better performance on 32-bit + // processors. + uint32 part0 = static_cast(value ); + uint32 part1 = static_cast(value >> 28); + uint32 part2 = static_cast(value >> 56); + + int size; + + // Here we can't really optimize for small numbers, since the value is + // split into three parts. Cheking for numbers < 128, for instance, + // would require three comparisons, since you'd have to make sure part1 + // and part2 are zero. However, if the caller is using 64-bit integers, + // it is likely that they expect the numbers to often be very large, so + // we probably don't want to optimize for small numbers anyway. Thus, + // we end up with a hardcoded binary search tree... + if (part2 == 0) { + if (part1 == 0) { + if (part0 < (1 << 14)) { + if (part0 < (1 << 7)) { + size = 1; goto size1; + } else { + size = 2; goto size2; + } + } else { + if (part0 < (1 << 21)) { + size = 3; goto size3; + } else { + size = 4; goto size4; + } + } + } else { + if (part1 < (1 << 14)) { + if (part1 < (1 << 7)) { + size = 5; goto size5; + } else { + size = 6; goto size6; + } + } else { + if (part1 < (1 << 21)) { + size = 7; goto size7; + } else { + size = 8; goto size8; + } + } + } + } else { + if (part2 < (1 << 7)) { + size = 9; goto size9; + } else { + size = 10; goto size10; + } + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + + size10: target[9] = static_cast((part2 >> 7) | 0x80); + size9 : target[8] = static_cast((part2 ) | 0x80); + size8 : target[7] = static_cast((part1 >> 21) | 0x80); + size7 : target[6] = static_cast((part1 >> 14) | 0x80); + size6 : target[5] = static_cast((part1 >> 7) | 0x80); + size5 : target[4] = static_cast((part1 ) | 0x80); + size4 : target[3] = static_cast((part0 >> 21) | 0x80); + size3 : target[2] = static_cast((part0 >> 14) | 0x80); + size2 : target[1] = static_cast((part0 >> 7) | 0x80); + size1 : target[0] = static_cast((part0 ) | 0x80); + + target[size-1] &= 0x7F; + Advance(size); + return true; + } else { + // Slow path: This write might cross the end of the buffer, so we + // compose the bytes first then use WriteRaw(). + uint8 bytes[kMaxVarintBytes]; + int size = 0; + while (value > 0x7F) { + bytes[size++] = (static_cast(value) & 0x7F) | 0x80; + value >>= 7; + } + bytes[size++] = static_cast(value) & 0x7F; + return WriteRaw(bytes, size); + } +} + +bool CodedOutputStream::Refresh() { + void* void_buffer; + if (output_->Next(&void_buffer, &buffer_size_)) { + buffer_ = reinterpret_cast(void_buffer); + total_bytes_ += buffer_size_; + return true; + } else { + buffer_ = NULL; + buffer_size_ = 0; + return false; + } +} + +int CodedOutputStream::VarintSize32Fallback(uint32 value) { + if (value < (1 << 7)) { + return 1; + } else if (value < (1 << 14)) { + return 2; + } else if (value < (1 << 21)) { + return 3; + } else if (value < (1 << 28)) { + return 4; + } else { + return 5; + } +} + +int CodedOutputStream::VarintSize64(uint64 value) { + if (value < (1ull << 35)) { + if (value < (1ull << 7)) { + return 1; + } else if (value < (1ull << 14)) { + return 2; + } else if (value < (1ull << 21)) { + return 3; + } else if (value < (1ull << 28)) { + return 4; + } else { + return 5; + } + } else { + if (value < (1ull << 42)) { + return 6; + } else if (value < (1ull << 49)) { + return 7; + } else if (value < (1ull << 56)) { + return 8; + } else if (value < (1ull << 63)) { + return 9; + } else { + return 10; + } + } +} + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h new file mode 100644 index 00000000..91e5c56a --- /dev/null +++ b/src/google/protobuf/io/coded_stream.h @@ -0,0 +1,592 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains the CodedInputStream and CodedOutputStream classes, +// which wrap a ZeroCopyInputStream or ZeroCopyOutputStream, respectively, +// and allow you to read or write individual pieces of data in various +// formats. In particular, these implement the varint encoding for +// integers, a simple variable-length encoding in which smaller numbers +// take fewer bytes. +// +// Typically these classes will only be used internally by the protocol +// buffer library in order to encode and decode protocol buffers. Clients +// of the library only need to know about this class if they wish to write +// custom message parsing or serialization procedures. +// +// CodedOutputStream example: +// // Write some data to "myfile". First we write a 4-byte "magic number" +// // to identify the file type, then write a length-delimited string. The +// // string is composed of a varint giving the length followed by the raw +// // bytes. +// int fd = open("myfile", O_WRONLY); +// ZeroCopyOutputStream* raw_output = new FileOutputStream(fd); +// CodedOutputStream* coded_output = new CodedOutputStream(raw_output); +// +// int magic_number = 1234; +// char text[] = "Hello world!"; +// coded_output->WriteLittleEndian32(magic_number); +// coded_output->WriteVarint32(strlen(text)); +// coded_output->WriteRaw(text, strlen(text)); +// +// delete coded_output; +// delete raw_output; +// close(fd); +// +// CodedInputStream example: +// // Read a file created by the above code. +// int fd = open("myfile", O_RDONLY); +// ZeroCopyInputStream* raw_input = new FileInputStream(fd); +// CodedInputStream coded_input = new CodedInputStream(raw_input); +// +// coded_input->ReadLittleEndian32(&magic_number); +// if (magic_number != 1234) { +// cerr << "File not in expected format." << endl; +// return; +// } +// +// uint32 size; +// coded_input->ReadVarint32(&size); +// +// char* text = new char[size + 1]; +// coded_input->ReadRaw(buffer, size); +// text[size] = '\0'; +// +// delete coded_input; +// delete raw_input; +// close(fd); +// +// cout << "Text is: " << text << endl; +// delete [] text; +// +// For those who are interested, varint encoding is defined as follows: +// +// The encoding operates on unsigned integers of up to 64 bits in length. +// Each byte of the encoded value has the format: +// * bits 0-6: Seven bits of the number being encoded. +// * bit 7: Zero if this is the last byte in the encoding (in which +// case all remaining bits of the number are zero) or 1 if +// more bytes follow. +// The first byte contains the least-significant 7 bits of the number, the +// second byte (if present) contains the next-least-significant 7 bits, +// and so on. So, the binary number 1011000101011 would be encoded in two +// bytes as "10101011 00101100". +// +// In theory, varint could be used to encode integers of any length. +// However, for practicality we set a limit at 64 bits. The maximum encoded +// length of a number is thus 10 bytes. + +#ifndef GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ +#define GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ + +#include +#include + +namespace google { + +namespace protobuf { +namespace io { + +// Defined in this file. +class CodedInputStream; +class CodedOutputStream; + +// Defined in other files. +class ZeroCopyInputStream; // zero_copy_stream.h +class ZeroCopyOutputStream; // zero_copy_stream.h + +// Class which reads and decodes binary data which is composed of varint- +// encoded integers and fixed-width pieces. Wraps a ZeroCopyInputStream. +// Most users will not need to deal with CodedInputStream. +// +// Most methods of CodedInputStream that return a bool return false if an +// underlying I/O error occurs or if the data is malformed. Once such a +// failure occurs, the CodedInputStream is broken and is no longer useful. +class LIBPROTOBUF_EXPORT CodedInputStream { + public: + // Create a CodedInputStream that reads from the given ZeroCopyInputStream. + explicit CodedInputStream(ZeroCopyInputStream* input); + + // Destroy the CodedInputStream and position the underlying + // ZeroCopyInputStream at the first unread byte. If an error occurred while + // reading (causing a method to return false), then the exact position of + // the input stream may be anywhere between the last value that was read + // successfully and the stream's byte limit. + ~CodedInputStream(); + + + // Skips a number of bytes. Returns false if an underlying read error + // occurs. + bool Skip(int count); + + // Read raw bytes, copying them into the given buffer. + bool ReadRaw(void* buffer, int size); + + // Like ReadRaw, but reads into a string. + // + // Implementation Note: ReadString() grows the string gradually as it + // reads in the data, rather than allocating the entire requested size + // upfront. This prevents denial-of-service attacks in which a client + // could claim that a string is going to be MAX_INT bytes long in order to + // crash the server because it can't allocate this much space at once. + bool ReadString(string* buffer, int size); + + + // Read a 32-bit little-endian integer. + bool ReadLittleEndian32(uint32* value); + // Read a 64-bit little-endian integer. + bool ReadLittleEndian64(uint64* value); + + // Read an unsigned integer with Varint encoding, truncating to 32 bits. + // Reading a 32-bit value is equivalent to reading a 64-bit one and casting + // it to uint32, but may be more efficient. + bool ReadVarint32(uint32* value); + // Read an unsigned integer with Varint encoding. + bool ReadVarint64(uint64* value); + + // Read a tag. This calls ReadVarint32() and returns the result, or returns + // zero (which is not a valid tag) if ReadVarint32() fails. Also, it updates + // the last tag value, which can be checked with LastTagWas(). + // Always inline because this is only called in once place per parse loop + // but it is called for every iteration of said loop, so it should be fast. + // GCC doesn't want to inline this by default. + uint32 ReadTag() GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + + // Usually returns true if calling ReadVarint32() now would produce the given + // value. Will always return false if ReadVarint32() would not return the + // given value. If ExpectTag() returns true, it also advances past + // the varint. For best performance, use a compile-time constant as the + // parameter. + // Always inline because this collapses to a small number of instructions + // when given a constant parameter, but GCC doesn't want to inline by default. + bool ExpectTag(uint32 expected) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + + // Usually returns true if no more bytes can be read. Always returns false + // if more bytes can be read. If ExpectAtEnd() returns true, a subsequent + // call to LastTagWas() will act as if ReadTag() had been called and returned + // zero, and ConsumedEntireMessage() will return true. + bool ExpectAtEnd(); + + // If the last call to ReadTag() returned the given value, returns true. + // Otherwise, returns false; + // + // This is needed because parsers for some types of embedded messages + // (with field type TYPE_GROUP) don't actually know that they've reached the + // end of a message until they see an ENDGROUP tag, which was actually part + // of the enclosing message. The enclosing message would like to check that + // tag to make sure it had the right number, so it calls LastTagWas() on + // return from the embedded parser to check. + bool LastTagWas(uint32 expected); + + // When parsing message (but NOT a group), this method must be called + // immediately after MergeFromCodedStream() returns (if it returns true) + // to further verify that the message ended in a legitimate way. For + // example, this verifies that parsing did not end on an end-group tag. + // It also checks for some cases where, due to optimizations, + // MergeFromCodedStream() can incorrectly return true. + bool ConsumedEntireMessage(); + + // Limits ---------------------------------------------------------- + // Limits are used when parsing length-delimited embedded messages. + // After the message's length is read, PushLimit() is used to prevent + // the CodedInputStream from reading beyond that length. Once the + // embedded message has been parsed, PopLimit() is called to undo the + // limit. + + // Opaque type used with PushLimit() and PopLimit(). Do not modify + // values of this type yourself. The only reason that this isn't a + // struct with private internals is for efficiency. + typedef int Limit; + + // Places a limit on the number of bytes that the stream may read, + // starting from the current position. Once the stream hits this limit, + // it will act like the end of the input has been reached until PopLimit() + // is called. + // + // As the names imply, the stream conceptually has a stack of limits. The + // shortest limit on the stack is always enforced, even if it is not the + // top limit. + // + // The value returned by PushLimit() is opaque to the caller, and must + // be passed unchanged to the corresponding call to PopLimit(). + Limit PushLimit(int byte_limit); + + // Pops the last limit pushed by PushLimit(). The input must be the value + // returned by that call to PushLimit(). + void PopLimit(Limit limit); + + // Returns the number of bytes left until the nearest limit on the + // stack is hit, or -1 if no limits are in place. + int BytesUntilLimit(); + + // Total Bytes Limit ----------------------------------------------- + // To prevent malicious users from sending excessively large messages + // and causing integer overflows or memory exhaustion, CodedInputStream + // imposes a hard limit on the total number of bytes it will read. + + // Sets the maximum number of bytes that this CodedInputStream will read + // before refusing to continue. To prevent integer overflows in the + // protocol buffers implementation, as well as to prevent servers from + // allocating enormous amounts of memory to hold parsed messages, the + // maximum message length should be limited to the shortest length that + // will not harm usability. The theoretical shortest message that could + // cause integer overflows is 512MB. The default limit is 64MB. Apps + // should set shorter limits if possible. If warning_threshold is not -1, + // a warning will be printed to stderr after warning_threshold bytes are + // read. An error will always be printed to stderr if the limit is + // reached. + // + // This is unrelated to PushLimit()/PopLimit(). + // + // Hint: If you are reading this because your program is printing a + // warning about dangerously large protocol messages, you may be + // confused about what to do next. The best option is to change your + // design such that excessively large messages are not necessary. + // For example, try to design file formats to consist of many small + // messages rather than a single large one. If this is infeasible, + // you will need to increase the limit. Chances are, though, that + // your code never constructs a CodedInputStream on which the limit + // can be set. You probably parse messages by calling things like + // Message::ParseFromString(). In this case, you will need to change + // your code to instead construct some sort of ZeroCopyInputStream + // (e.g. an ArrayInputStream), construct a CodedInputStream around + // that, then call Message::ParseFromCodedStream() instead. Then + // you can adjust the limit. Yes, it's more work, but you're doing + // something unusual. + void SetTotalBytesLimit(int total_bytes_limit, int warning_threshold); + + // Recursion Limit ------------------------------------------------- + // To prevent corrupt or malicious messages from causing stack overflows, + // we must keep track of the depth of recursion when parsing embedded + // messages and groups. CodedInputStream keeps track of this because it + // is the only object that is passed down the stack during parsing. + + // Sets the maximum recursion depth. The default is 64. + void SetRecursionLimit(int limit); + + // Increments the current recursion depth. Returns true if the depth is + // under the limit, false if it has gone over. + bool IncrementRecursionDepth(); + + // Decrements the recursion depth. + void DecrementRecursionDepth(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodedInputStream); + + ZeroCopyInputStream* input_; + const uint8* buffer_; + int buffer_size_; // size of current buffer + int total_bytes_read_; // total bytes read from input_, including + // the current buffer + + // If total_bytes_read_ surpasses INT_MAX, we record the extra bytes here + // so that we can BackUp() on destruction. + int overflow_bytes_; + + // LastTagWas() stuff. + uint32 last_tag_; // result of last ReadTag(). + + // This is set true by ReadVarint32Fallback() if it is called when exactly + // at EOF, or by ExpectAtEnd() when it returns true. This happens when we + // reach the end of a message and attempt to read another tag. + bool legitimate_message_end_; + + // See EnableAliasing(). + bool aliasing_enabled_; + + // Limits + Limit current_limit_; // if position = -1, no limit is applied + + // For simplicity, if the current buffer crosses a limit (either a normal + // limit created by PushLimit() or the total bytes limit), buffer_size_ + // only tracks the number of bytes before that limit. This field + // contains the number of bytes after it. Note that this implies that if + // buffer_size_ == 0 and buffer_size_after_limit_ > 0, we know we've + // hit a limit. However, if both are zero, it doesn't necessarily mean + // we aren't at a limit -- the buffer may have ended exactly at the limit. + int buffer_size_after_limit_; + + // Maximum number of bytes to read, period. This is unrelated to + // current_limit_. Set using SetTotalBytesLimit(). + int total_bytes_limit_; + int total_bytes_warning_threshold_; + + // Current recursion depth, controlled by IncrementRecursionDepth() and + // DecrementRecursionDepth(). + int recursion_depth_; + // Recursion depth limit, set by SetRecursionLimit(). + int recursion_limit_; + + // Advance the buffer by a given number of bytes. + void Advance(int amount); + + // Recomputes the value of buffer_size_after_limit_. Must be called after + // current_limit_ or total_bytes_limit_ changes. + void RecomputeBufferLimits(); + + // Writes an error message saying that we hit total_bytes_limit_. + void PrintTotalBytesLimitError(); + + // Called when the buffer runs out to request more data. Implies an + // Advance(buffer_size_). + bool Refresh(); + + bool ReadVarint32Fallback(uint32* value); +}; + +// Class which encodes and writes binary data which is composed of varint- +// encoded integers and fixed-width pieces. Wraps a ZeroCopyOutputStream. +// Most users will not need to deal with CodedOutputStream. +// +// Most methods of CodedOutputStream which return a bool return false if an +// underlying I/O error occurs. Once such a failure occurs, the +// CodedOutputStream is broken and is no longer useful. +class LIBPROTOBUF_EXPORT CodedOutputStream { + public: + // Create an CodedOutputStream that writes to the given ZeroCopyOutputStream. + explicit CodedOutputStream(ZeroCopyOutputStream* output); + + // Destroy the CodedOutputStream and position the underlying + // ZeroCopyOutputStream immediately after the last byte written. + ~CodedOutputStream(); + + // Write raw bytes, copying them from the given buffer. + bool WriteRaw(const void* buffer, int size); + + // Equivalent to WriteRaw(str.data(), str.size()). + bool WriteString(const string& str); + + + // Write a 32-bit little-endian integer. + bool WriteLittleEndian32(uint32 value); + // Write a 64-bit little-endian integer. + bool WriteLittleEndian64(uint64 value); + + // Write an unsigned integer with Varint encoding. Writing a 32-bit value + // is equivalent to casting it to uint64 and writing it as a 64-bit value, + // but may be more efficient. + bool WriteVarint32(uint32 value); + // Write an unsigned integer with Varint encoding. + bool WriteVarint64(uint64 value); + + // Equivalent to WriteVarint32() except when the value is negative, + // in which case it must be sign-extended to a full 10 bytes. + bool WriteVarint32SignExtended(int32 value); + + // This is identical to WriteVarint32(), but optimized for writing tags. + // In particular, if the input is a compile-time constant, this method + // compiles down to a couple instructions. + // Always inline because otherwise the aformentioned optimization can't work, + // but GCC by default doesn't want to inline this. + bool WriteTag(uint32 value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + + // Returns the number of bytes needed to encode the given value as a varint. + static int VarintSize32(uint32 value); + // Returns the number of bytes needed to encode the given value as a varint. + static int VarintSize64(uint64 value); + + // If negative, 10 bytes. Otheriwse, same as VarintSize32(). + static int VarintSize32SignExtended(int32 value); + + // Returns the total number of bytes written since this object was created. + inline int ByteCount() const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodedOutputStream); + + ZeroCopyOutputStream* output_; + uint8* buffer_; + int buffer_size_; + int total_bytes_; // Sum of sizes of all buffers seen so far. + + // Advance the buffer by a given number of bytes. + void Advance(int amount); + + // Called when the buffer runs out to request more data. Implies an + // Advance(buffer_size_). + bool Refresh(); + + bool WriteVarint32Fallback(uint32 value); + static int VarintSize32Fallback(uint32 value); +}; + +// inline methods ==================================================== +// The vast majority of varints are only one byte. These inline +// methods optimize for that case. + +inline bool CodedInputStream::ReadVarint32(uint32* value) { + if (buffer_size_ != 0 && *buffer_ < 0x80) { + *value = *buffer_; + Advance(1); + return true; + } else { + return ReadVarint32Fallback(value); + } +} + +inline uint32 CodedInputStream::ReadTag() { + if (buffer_size_ != 0 && buffer_[0] < 0x80) { + last_tag_ = buffer_[0]; + Advance(1); + return last_tag_; + } else if (buffer_size_ >= 2 && buffer_[1] < 0x80) { + last_tag_ = (buffer_[0] & 0x7f) + (buffer_[1] << 7); + Advance(2); + return last_tag_; + } else if (ReadVarint32Fallback(&last_tag_)) { + return last_tag_; + } else { + last_tag_ = 0; + return 0; + } +} + +inline bool CodedInputStream::LastTagWas(uint32 expected) { + return last_tag_ == expected; +} + +inline bool CodedInputStream::ConsumedEntireMessage() { + return legitimate_message_end_; +} + +inline bool CodedInputStream::ExpectTag(uint32 expected) { + if (expected < (1 << 7)) { + if (buffer_size_ != 0 && buffer_[0] == expected) { + Advance(1); + return true; + } else { + return false; + } + } else if (expected < (1 << 14)) { + if (buffer_size_ >= 2 && + buffer_[0] == static_cast(expected | 0x80) && + buffer_[1] == static_cast(expected >> 7)) { + Advance(2); + return true; + } else { + return false; + } + } else { + // Don't bother optimizing for larger values. + return false; + } +} + +inline bool CodedInputStream::ExpectAtEnd() { + // If we are at a limit we know no more bytes can be read. Otherwise, it's + // hard to say without calling Refresh(), and we'd rather not do that. + + if (buffer_size_ == 0 && buffer_size_after_limit_ != 0) { + last_tag_ = 0; // Pretend we called ReadTag()... + legitimate_message_end_ = true; // ... and it hit EOF. + return true; + } else { + return false; + } +} + +inline bool CodedOutputStream::WriteVarint32(uint32 value) { + if (value < 0x80 && buffer_size_ > 0) { + *buffer_ = value; + Advance(1); + return true; + } else { + return WriteVarint32Fallback(value); + } +} + +inline bool CodedOutputStream::WriteVarint32SignExtended(int32 value) { + if (value < 0) { + return WriteVarint64(static_cast(value)); + } else { + return WriteVarint32(static_cast(value)); + } +} + +inline bool CodedOutputStream::WriteTag(uint32 value) { + if (value < (1 << 7)) { + if (buffer_size_ != 0) { + buffer_[0] = value; + Advance(1); + return true; + } + } else if (value < (1 << 14)) { + if (buffer_size_ >= 2) { + buffer_[0] = static_cast(value | 0x80); + buffer_[1] = static_cast(value >> 7); + Advance(2); + return true; + } + } + return WriteVarint32Fallback(value); +} + +inline int CodedOutputStream::VarintSize32(uint32 value) { + if (value < (1 << 7)) { + return 1; + } else { + return VarintSize32Fallback(value); + } +} + +inline int CodedOutputStream::VarintSize32SignExtended(int32 value) { + if (value < 0) { + return 10; // TODO(kenton): Make this a symbolic constant. + } else { + return VarintSize32(static_cast(value)); + } +} + +inline bool CodedOutputStream::WriteString(const string& str) { + return WriteRaw(str.data(), str.size()); +} + +inline int CodedOutputStream::ByteCount() const { + return total_bytes_ - buffer_size_; +} + +inline void CodedInputStream::Advance(int amount) { + buffer_ += amount; + buffer_size_ -= amount; +} + +inline void CodedOutputStream::Advance(int amount) { + buffer_ += amount; + buffer_size_ -= amount; +} + +inline void CodedInputStream::SetRecursionLimit(int limit) { + recursion_limit_ = limit; +} + +inline bool CodedInputStream::IncrementRecursionDepth() { + ++recursion_depth_; + return recursion_depth_ <= recursion_limit_; +} + +inline void CodedInputStream::DecrementRecursionDepth() { + if (recursion_depth_ > 0) --recursion_depth_; +} + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc new file mode 100644 index 00000000..c1a88349 --- /dev/null +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -0,0 +1,929 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains tests and benchmarks. + +#include + +#include + +#include + +#include +#include +#include +#include +#include + + +// This declares an unsigned long long integer literal in a portable way. +// (The original macro is way too big and ruins my formatting.) +#undef ULL +#define ULL(x) GOOGLE_ULONGLONG(x) + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// =================================================================== +// Data-Driven Test Infrastructure + +// TEST_1D and TEST_2D are macros I'd eventually like to see added to +// gTest. These macros can be used to declare tests which should be +// run multiple times, once for each item in some input array. TEST_1D +// tests all cases in a single input array. TEST_2D tests all +// combinations of cases from two arrays. The arrays must be statically +// defined such that the GOOGLE_ARRAYSIZE() macro works on them. Example: +// +// int kCases[] = {1, 2, 3, 4} +// TEST_1D(MyFixture, MyTest, kCases) { +// EXPECT_GT(kCases_case, 0); +// } +// +// This test iterates through the numbers 1, 2, 3, and 4 and tests that +// they are all grater than zero. In case of failure, the exact case +// which failed will be printed. The case type must be printable using +// ostream::operator<<. + +#define TEST_1D(FIXTURE, NAME, CASES) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType& CASES##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES); i++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES " case #" << i << ": " << CASES[i]); \ + DoSingleCase(CASES[i]); \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType& CASES##_case) + +#define TEST_2D(FIXTURE, NAME, CASES1, CASES2) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES1); i++) { \ + for (int j = 0; j < GOOGLE_ARRAYSIZE(CASES2); j++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES1 " case #" << i << ": " << CASES1[i] << ", " \ + << #CASES2 " case #" << j << ": " << CASES2[j]); \ + DoSingleCase(CASES1[i], CASES2[j]); \ + } \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case) + +// =================================================================== + +class CodedStreamTest : public testing::Test { + protected: + static const int kBufferSize = 1024 * 64; + static uint8 buffer_[kBufferSize]; +}; + +uint8 CodedStreamTest::buffer_[CodedStreamTest::kBufferSize]; + +// We test each operation over a variety of block sizes to insure that +// we test cases where reads or writes cross buffer boundaries, cases +// where they don't, and cases where there is so much buffer left that +// we can use special optimized paths that don't worry about bounds +// checks. +const int kBlockSizes[] = {1, 2, 3, 5, 7, 13, 32, 1024}; + +// ------------------------------------------------------------------- +// Varint tests. + +struct VarintCase { + uint8 bytes[10]; // Encoded bytes. + int size; // Encoded size, in bytes. + uint64 value; // Parsed value. +}; + +inline std::ostream& operator<<(std::ostream& os, const VarintCase& c) { + return os << c.value; +} + +VarintCase kVarintCases[] = { + // 32-bit values + {{0x00} , 1, 0}, + {{0x01} , 1, 1}, + {{0x7f} , 1, 127}, + {{0xa2, 0x74}, 2, (0x22 << 0) | (0x74 << 7)}, // 14882 + {{0xbe, 0xf7, 0x92, 0x84, 0x0b}, 5, // 2961488830 + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (ULL(0x0b) << 28)}, + + // 64-bit + {{0xbe, 0xf7, 0x92, 0x84, 0x1b}, 5, // 7256456126 + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (ULL(0x1b) << 28)}, + {{0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49}, 8, // 41256202580718336 + (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | + (ULL(0x43) << 28) | (ULL(0x49) << 35) | (ULL(0x24) << 42) | + (ULL(0x49) << 49)}, + // 11964378330978735131 + {{0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01}, 10, + (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | + (ULL(0x3b) << 28) | (ULL(0x56) << 35) | (ULL(0x00) << 42) | + (ULL(0x05) << 49) | (ULL(0x26) << 56) | (ULL(0x01) << 63)}, +}; + +TEST_2D(CodedStreamTest, ReadVarint32, kVarintCases, kBlockSizes) { + memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint32 value; + EXPECT_TRUE(coded_input.ReadVarint32(&value)); + EXPECT_EQ(static_cast(kVarintCases_case.value), value); + } + + EXPECT_EQ(kVarintCases_case.size, input.ByteCount()); +} + +TEST_2D(CodedStreamTest, ReadTag, kVarintCases, kBlockSizes) { + memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint32 expected_value = static_cast(kVarintCases_case.value); + EXPECT_EQ(expected_value, coded_input.ReadTag()); + + EXPECT_TRUE(coded_input.LastTagWas(expected_value)); + EXPECT_FALSE(coded_input.LastTagWas(expected_value + 1)); + } + + EXPECT_EQ(kVarintCases_case.size, input.ByteCount()); +} + +TEST_1D(CodedStreamTest, ExpectTag, kVarintCases) { + // Leave one byte at the beginning of the buffer so we can read it + // to force the first buffer to be loaded. + buffer_[0] = '\0'; + memcpy(buffer_ + 1, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_)); + + { + CodedInputStream coded_input(&input); + + // Read one byte to force coded_input.Refill() to be called. Otherwise, + // ExpectTag() will return a false negative. + uint8 dummy; + coded_input.ReadRaw(&dummy, 1); + EXPECT_EQ((uint)'\0', (uint)dummy); + + uint32 expected_value = static_cast(kVarintCases_case.value); + + // ExpectTag() produces false negatives for large values. + if (kVarintCases_case.size <= 2) { + EXPECT_FALSE(coded_input.ExpectTag(expected_value + 1)); + EXPECT_TRUE(coded_input.ExpectTag(expected_value)); + } else { + EXPECT_FALSE(coded_input.ExpectTag(expected_value)); + } + } + + if (kVarintCases_case.size <= 2) { + EXPECT_EQ(kVarintCases_case.size + 1, input.ByteCount()); + } else { + EXPECT_EQ(1, input.ByteCount()); + } +} + +TEST_2D(CodedStreamTest, ReadVarint64, kVarintCases, kBlockSizes) { + memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_TRUE(coded_input.ReadVarint64(&value)); + EXPECT_EQ(kVarintCases_case.value, value); + } + + EXPECT_EQ(kVarintCases_case.size, input.ByteCount()); +} + +TEST_2D(CodedStreamTest, WriteVarint32, kVarintCases, kBlockSizes) { + if (kVarintCases_case.value > ULL(0x00000000FFFFFFFF)) { + // Skip this test for the 64-bit values. + return; + } + + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteVarint32( + static_cast(kVarintCases_case.value))); + + EXPECT_EQ(kVarintCases_case.size, coded_output.ByteCount()); + } + + EXPECT_EQ(kVarintCases_case.size, output.ByteCount()); + EXPECT_EQ(0, + memcmp(buffer_, kVarintCases_case.bytes, kVarintCases_case.size)); +} + +TEST_2D(CodedStreamTest, WriteVarint64, kVarintCases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteVarint64(kVarintCases_case.value)); + + EXPECT_EQ(kVarintCases_case.size, coded_output.ByteCount()); + } + + EXPECT_EQ(kVarintCases_case.size, output.ByteCount()); + EXPECT_EQ(0, + memcmp(buffer_, kVarintCases_case.bytes, kVarintCases_case.size)); +} + +// This test causes gcc 3.3.5 (and earlier?) to give the cryptic error: +// "sorry, unimplemented: `method_call_expr' not supported by dump_expr" +#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) + +int32 kSignExtendedVarintCases[] = { + 0, 1, -1, 1237894, -37895138 +}; + +TEST_2D(CodedStreamTest, WriteVarint32SignExtended, + kSignExtendedVarintCases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteVarint32SignExtended( + kSignExtendedVarintCases_case)); + + if (kSignExtendedVarintCases_case < 0) { + EXPECT_EQ(10, coded_output.ByteCount()); + } else { + EXPECT_LE(coded_output.ByteCount(), 5); + } + } + + if (kSignExtendedVarintCases_case < 0) { + EXPECT_EQ(10, output.ByteCount()); + } else { + EXPECT_LE(output.ByteCount(), 5); + } + + // Read value back in as a varint64 and insure it matches. + ArrayInputStream input(buffer_, sizeof(buffer_)); + + { + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_TRUE(coded_input.ReadVarint64(&value)); + + EXPECT_EQ(kSignExtendedVarintCases_case, static_cast(value)); + } + + EXPECT_EQ(output.ByteCount(), input.ByteCount()); +} + +#endif + + +// ------------------------------------------------------------------- +// Varint failure test. + +struct VarintErrorCase { + uint8 bytes[12]; + int size; + bool can_parse; +}; + +inline std::ostream& operator<<(std::ostream& os, const VarintErrorCase& c) { + return os << "size " << c.size; +} + +const VarintErrorCase kVarintErrorCases[] = { + // Control case. (Insures that there isn't something else wrong that + // makes parsing always fail.) + {{0x00}, 1, true}, + + // No input data. + {{}, 0, false}, + + // Input ends unexpectedly. + {{0xf0, 0xab}, 2, false}, + + // Input ends unexpectedly after 32 bits. + {{0xf0, 0xab, 0xc9, 0x9a, 0xf8, 0xb2}, 6, false}, + + // Longer than 10 bytes. + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}, + 11, false}, +}; + +TEST_2D(CodedStreamTest, ReadVarint32Error, kVarintErrorCases, kBlockSizes) { + memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size); + ArrayInputStream input(buffer_, kVarintErrorCases_case.size, + kBlockSizes_case); + CodedInputStream coded_input(&input); + + uint32 value; + EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint32(&value)); +} + +TEST_2D(CodedStreamTest, ReadVarint64Error, kVarintErrorCases, kBlockSizes) { + memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size); + ArrayInputStream input(buffer_, kVarintErrorCases_case.size, + kBlockSizes_case); + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint64(&value)); +} + +// ------------------------------------------------------------------- +// VarintSize + +struct VarintSizeCase { + uint64 value; + int size; +}; + +inline std::ostream& operator<<(std::ostream& os, const VarintSizeCase& c) { + return os << c.value; +} + +VarintSizeCase kVarintSizeCases[] = { + {0u, 1}, + {1u, 1}, + {127u, 1}, + {128u, 2}, + {758923u, 3}, + {4000000000u, 5}, + {ULL(41256202580718336), 8}, + {ULL(11964378330978735131), 10}, +}; + +TEST_1D(CodedStreamTest, VarintSize32, kVarintSizeCases) { + if (kVarintSizeCases_case.value > 0xffffffffu) { + // Skip 64-bit values. + return; + } + + EXPECT_EQ(kVarintSizeCases_case.size, + CodedOutputStream::VarintSize32( + static_cast(kVarintSizeCases_case.value))); +} + +TEST_1D(CodedStreamTest, VarintSize64, kVarintSizeCases) { + EXPECT_EQ(kVarintSizeCases_case.size, + CodedOutputStream::VarintSize64(kVarintSizeCases_case.value)); +} + +// ------------------------------------------------------------------- +// Fixed-size int tests + +struct Fixed32Case { + uint8 bytes[sizeof(uint32)]; // Encoded bytes. + uint32 value; // Parsed value. +}; + +struct Fixed64Case { + uint8 bytes[sizeof(uint64)]; // Encoded bytes. + uint64 value; // Parsed value. +}; + +inline std::ostream& operator<<(std::ostream& os, const Fixed32Case& c) { + return os << "0x" << hex << c.value << dec; +} + +inline std::ostream& operator<<(std::ostream& os, const Fixed64Case& c) { + return os << "0x" << hex << c.value << dec; +} + +Fixed32Case kFixed32Cases[] = { + {{0xef, 0xcd, 0xab, 0x90}, 0x90abcdefu}, + {{0x12, 0x34, 0x56, 0x78}, 0x78563412u}, +}; + +Fixed64Case kFixed64Cases[] = { + {{0xef, 0xcd, 0xab, 0x90, 0x12, 0x34, 0x56, 0x78}, ULL(0x7856341290abcdef)}, + {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, ULL(0x8877665544332211)}, +}; + +TEST_2D(CodedStreamTest, ReadLittleEndian32, kFixed32Cases, kBlockSizes) { + memcpy(buffer_, kFixed32Cases_case.bytes, sizeof(kFixed32Cases_case.bytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint32 value; + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(kFixed32Cases_case.value, value); + } + + EXPECT_EQ(sizeof(uint32), input.ByteCount()); +} + +TEST_2D(CodedStreamTest, ReadLittleEndian64, kFixed64Cases, kBlockSizes) { + memcpy(buffer_, kFixed64Cases_case.bytes, sizeof(kFixed64Cases_case.bytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_TRUE(coded_input.ReadLittleEndian64(&value)); + EXPECT_EQ(kFixed64Cases_case.value, value); + } + + EXPECT_EQ(sizeof(uint64), input.ByteCount()); +} + +TEST_2D(CodedStreamTest, WriteLittleEndian32, kFixed32Cases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteLittleEndian32(kFixed32Cases_case.value)); + + EXPECT_EQ(sizeof(uint32), coded_output.ByteCount()); + } + + EXPECT_EQ(sizeof(uint32), output.ByteCount()); + EXPECT_EQ(0, memcmp(buffer_, kFixed32Cases_case.bytes, sizeof(uint32))); +} + +TEST_2D(CodedStreamTest, WriteLittleEndian64, kFixed64Cases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteLittleEndian64(kFixed64Cases_case.value)); + + EXPECT_EQ(sizeof(uint64), coded_output.ByteCount()); + } + + EXPECT_EQ(sizeof(uint64), output.ByteCount()); + EXPECT_EQ(0, memcmp(buffer_, kFixed64Cases_case.bytes, sizeof(uint64))); +} + +// ------------------------------------------------------------------- +// Raw reads and writes + +const char kRawBytes[] = "Some bytes which will be writted and read raw."; + +TEST_1D(CodedStreamTest, ReadRaw, kBlockSizes) { + memcpy(buffer_, kRawBytes, sizeof(kRawBytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + char read_buffer[sizeof(kRawBytes)]; + + { + CodedInputStream coded_input(&input); + + EXPECT_TRUE(coded_input.ReadRaw(read_buffer, sizeof(kRawBytes))); + EXPECT_EQ(0, memcmp(kRawBytes, read_buffer, sizeof(kRawBytes))); + } + + EXPECT_EQ(sizeof(kRawBytes), input.ByteCount()); +} + +TEST_1D(CodedStreamTest, WriteRaw, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteRaw(kRawBytes, sizeof(kRawBytes))); + + EXPECT_EQ(sizeof(kRawBytes), coded_output.ByteCount()); + } + + EXPECT_EQ(sizeof(kRawBytes), output.ByteCount()); + EXPECT_EQ(0, memcmp(buffer_, kRawBytes, sizeof(kRawBytes))); +} + +TEST_1D(CodedStreamTest, ReadString, kBlockSizes) { + memcpy(buffer_, kRawBytes, sizeof(kRawBytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + string str; + EXPECT_TRUE(coded_input.ReadString(&str, strlen(kRawBytes))); + EXPECT_EQ(kRawBytes, str); + } + + EXPECT_EQ(strlen(kRawBytes), input.ByteCount()); +} + +// Check to make sure ReadString doesn't crash on impossibly large strings. +TEST_1D(CodedStreamTest, ReadStringImpossiblyLarge, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + string str; + // Try to read a gigabyte. + EXPECT_FALSE(coded_input.ReadString(&str, 1 << 30)); + } +} + + +// ------------------------------------------------------------------- +// Skip + +const char kSkipTestBytes[] = + ""; +const char kSkipOutputTestBytes[] = + "---------------------------------"; + +TEST_1D(CodedStreamTest, SkipInput, kBlockSizes) { + memcpy(buffer_, kSkipTestBytes, sizeof(kSkipTestBytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + string str; + EXPECT_TRUE(coded_input.ReadString(&str, strlen(""))); + EXPECT_EQ("", str); + EXPECT_TRUE(coded_input.Skip(strlen(""))); + EXPECT_TRUE(coded_input.ReadString(&str, strlen(""))); + EXPECT_EQ("", str); + } + + EXPECT_EQ(strlen(kSkipTestBytes), input.ByteCount()); +} + +// ------------------------------------------------------------------- +// Limits + +TEST_1D(CodedStreamTest, BasicLimit, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit = coded_input.PushLimit(8); + + // Read until we hit the limit. + uint32 value; + EXPECT_EQ(8, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + } + + EXPECT_EQ(12, input.ByteCount()); +} + +// Test what happens when we push two limits where the second (top) one is +// shorter. +TEST_1D(CodedStreamTest, SmallLimitOnTopOfBigLimit, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit1 = coded_input.PushLimit(8); + EXPECT_EQ(8, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit2 = coded_input.PushLimit(4); + + uint32 value; + + // Read until we hit limit2, the top and shortest limit. + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit2); + + // Read until we hit limit1. + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit1); + + // No more limits. + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + } + + EXPECT_EQ(12, input.ByteCount()); +} + +// Test what happens when we push two limits where the second (top) one is +// longer. In this case, the top limit is shortened to match the previous +// limit. +TEST_1D(CodedStreamTest, BigLimitOnTopOfSmallLimit, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit1 = coded_input.PushLimit(4); + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit2 = coded_input.PushLimit(8); + + uint32 value; + + // Read until we hit limit2. Except, wait! limit1 is shorter, so + // we end up hitting that first, despite having 4 bytes to go on + // limit2. + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit2); + + // OK, popped limit2, now limit1 is on top, which we've already hit. + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit1); + + // No more limits. + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + } + + EXPECT_EQ(8, input.ByteCount()); +} + +TEST_F(CodedStreamTest, ExpectAtEnd) { + // Test ExpectAtEnd(), which is based on limits. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + + EXPECT_FALSE(coded_input.ExpectAtEnd()); + + CodedInputStream::Limit limit = coded_input.PushLimit(4); + + uint32 value; + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_TRUE(coded_input.ExpectAtEnd()); + + coded_input.PopLimit(limit); + EXPECT_FALSE(coded_input.ExpectAtEnd()); +} + +TEST_F(CodedStreamTest, NegativeLimit) { + // Check what happens when we push a negative limit. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + + CodedInputStream::Limit limit = coded_input.PushLimit(-1234); + // BytesUntilLimit() returns -1 to mean "no limit", which actually means + // "the limit is INT_MAX relative to the beginning of the stream". + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + coded_input.PopLimit(limit); +} + +TEST_F(CodedStreamTest, NegativeLimitAfterReading) { + // Check what happens when we push a negative limit. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + ASSERT_TRUE(coded_input.Skip(128)); + + CodedInputStream::Limit limit = coded_input.PushLimit(-64); + // BytesUntilLimit() returns -1 to mean "no limit", which actually means + // "the limit is INT_MAX relative to the beginning of the stream". + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + coded_input.PopLimit(limit); +} + +TEST_F(CodedStreamTest, OverflowLimit) { + // Check what happens when we push a limit large enough that its absolute + // position is more than 2GB into the stream. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + ASSERT_TRUE(coded_input.Skip(128)); + + CodedInputStream::Limit limit = coded_input.PushLimit(INT_MAX); + // BytesUntilLimit() returns -1 to mean "no limit", which actually means + // "the limit is INT_MAX relative to the beginning of the stream". + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + coded_input.PopLimit(limit); +} + +TEST_F(CodedStreamTest, TotalBytesLimit) { + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + coded_input.SetTotalBytesLimit(16, -1); + + string str; + EXPECT_TRUE(coded_input.ReadString(&str, 16)); + + vector errors; + + { + ScopedMemoryLog error_log; + EXPECT_FALSE(coded_input.ReadString(&str, 1)); + errors = error_log.GetMessages(ERROR); + } + + ASSERT_EQ(1, errors.size()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, + "A protocol message was rejected because it was too big", errors[0]); + + coded_input.SetTotalBytesLimit(32, -1); + EXPECT_TRUE(coded_input.ReadString(&str, 16)); +} + +TEST_F(CodedStreamTest, TotalBytesLimitNotValidMessageEnd) { + // total_bytes_limit_ is not a valid place for a message to end. + + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + + // Set both total_bytes_limit and a regular limit at 16 bytes. + coded_input.SetTotalBytesLimit(16, -1); + CodedInputStream::Limit limit = coded_input.PushLimit(16); + + // Read 16 bytes. + string str; + EXPECT_TRUE(coded_input.ReadString(&str, 16)); + + // Read a tag. Should fail, but report being a valid endpoint since it's + // a regular limit. + EXPECT_EQ(0, coded_input.ReadTag()); + EXPECT_TRUE(coded_input.ConsumedEntireMessage()); + + // Pop the limit. + coded_input.PopLimit(limit); + + // Read a tag. Should fail, and report *not* being a valid endpoint, since + // this time we're hitting the total bytes limit. + EXPECT_EQ(0, coded_input.ReadTag()); + EXPECT_FALSE(coded_input.ConsumedEntireMessage()); +} + +TEST_F(CodedStreamTest, RecursionLimit) { + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + coded_input.SetRecursionLimit(4); + + // This is way too much testing for a counter. + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 1 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 2 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 3 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 4 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 5 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 6 + coded_input.DecrementRecursionDepth(); // 5 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 6 + coded_input.DecrementRecursionDepth(); // 5 + coded_input.DecrementRecursionDepth(); // 4 + coded_input.DecrementRecursionDepth(); // 3 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 4 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 5 + coded_input.DecrementRecursionDepth(); // 4 + coded_input.DecrementRecursionDepth(); // 3 + coded_input.DecrementRecursionDepth(); // 2 + coded_input.DecrementRecursionDepth(); // 1 + coded_input.DecrementRecursionDepth(); // 0 + coded_input.DecrementRecursionDepth(); // 0 + coded_input.DecrementRecursionDepth(); // 0 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 1 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 2 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 3 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 4 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 5 + + coded_input.SetRecursionLimit(6); + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 6 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 7 +} + +class ReallyBigInputStream : public ZeroCopyInputStream { + public: + ReallyBigInputStream() : backup_amount_(0), buffer_count_(0) {} + ~ReallyBigInputStream() {} + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size) { + // We only expect BackUp() to be called at the end. + EXPECT_EQ(0, backup_amount_); + + switch (buffer_count_++) { + case 0: + *data = buffer_; + *size = sizeof(buffer_); + return true; + case 1: + // Return an enormously large buffer that, when combined with the 1k + // returned already, should overflow the total_bytes_read_ counter in + // CodedInputStream. Note that we'll only read the first 1024 bytes + // of this buffer so it's OK that we have it point at buffer_. + *data = buffer_; + *size = INT_MAX; + return true; + default: + return false; + } + } + + void BackUp(int count) { + backup_amount_ = count; + } + + bool Skip(int count) { GOOGLE_LOG(FATAL) << "Not implemented."; return false; } + int64 ByteCount() const { GOOGLE_LOG(FATAL) << "Not implemented."; return 0; } + + int backup_amount_; + + private: + char buffer_[1024]; + int64 buffer_count_; +}; + +TEST_F(CodedStreamTest, InputOver2G) { + // CodedInputStream should gracefully handle input over 2G and call + // input.BackUp() with the correct number of bytes on destruction. + ReallyBigInputStream input; + + vector errors; + + { + ScopedMemoryLog error_log; + CodedInputStream coded_input(&input); + string str; + EXPECT_TRUE(coded_input.ReadString(&str, 512)); + EXPECT_TRUE(coded_input.ReadString(&str, 1024)); + errors = error_log.GetMessages(ERROR); + } + + EXPECT_EQ(INT_MAX - 512, input.backup_amount_); + EXPECT_EQ(0, errors.size()); +} + +// =================================================================== + + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/package_info.h b/src/google/protobuf/io/package_info.h new file mode 100644 index 00000000..8272d51d --- /dev/null +++ b/src/google/protobuf/io/package_info.h @@ -0,0 +1,40 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file exists solely to document the google::protobuf::io namespace. +// It is not compiled into anything, but it may be read by an automated +// documentation generator. + +namespace google { + +namespace protobuf { + +// Auxiliary classes used for I/O. +// +// The Protocol Buffer library uses the classes in this package to deal with +// I/O and encoding/decoding raw bytes. Most users will not need to +// deal with this package. However, users who want to adapt the system to +// work with their own I/O abstractions -- e.g., to allow Protocol Buffers +// to be read from a different kind of input stream without the need for a +// temporary buffer -- should take a closer look. +namespace io {} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc new file mode 100644 index 00000000..7b3e3de4 --- /dev/null +++ b/src/google/protobuf/io/printer.cc @@ -0,0 +1,165 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter) + : variable_delimiter_(variable_delimiter), + output_(output), + buffer_(NULL), + buffer_size_(0), + at_start_of_line_(true), + failed_(false) { +} + +Printer::~Printer() { + // Only BackUp() if we're sure we've successfully called Next() at least once. + if (buffer_size_ > 0) { + output_->BackUp(buffer_size_); + } +} + +void Printer::Print(const map& variables, const char* text) { + int size = strlen(text); + int pos = 0; // The number of bytes we've written so far. + + for (int i = 0; i < size; i++) { + if (text[i] == '\n') { + // Saw newline. If there is more text, we may need to insert an indent + // here. So, write what we have so far, including the '\n'. + Write(text + pos, i - pos + 1); + pos = i + 1; + + // Setting this true will cause the next Write() to insert an indent + // first. + at_start_of_line_ = true; + + } else if (text[i] == variable_delimiter_) { + // Saw the start of a variable name. + + // Write what we have so far. + Write(text + pos, i - pos); + pos = i + 1; + + // Find closing delimiter. + const char* end = strchr(text + pos, variable_delimiter_); + if (end == NULL) { + GOOGLE_LOG(DFATAL) << " Unclosed variable name."; + end = text + pos; + } + int endpos = end - text; + + string varname(text + pos, endpos - pos); + if (varname.empty()) { + // Two delimiters in a row reduce to a literal delimiter character. + Write(&variable_delimiter_, 1); + } else { + // Replace with the variable's value. + map::const_iterator iter = variables.find(varname); + if (iter == variables.end()) { + GOOGLE_LOG(DFATAL) << " Undefined variable: " << varname; + } else { + Write(iter->second.data(), iter->second.size()); + } + } + + // Advance past this variable. + i = endpos; + pos = endpos + 1; + } + } + + // Write the rest. + Write(text + pos, size - pos); +} + +void Printer::Print(const char* text) { + static map empty; + Print(empty, text); +} + +void Printer::Print(const char* text, + const char* variable, const string& value) { + map vars; + vars[variable] = value; + Print(vars, text); +} + +void Printer::Print(const char* text, + const char* variable1, const string& value1, + const char* variable2, const string& value2) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + Print(vars, text); +} + +void Printer::Indent() { + indent_ += " "; +} + +void Printer::Outdent() { + if (indent_.empty()) { + GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent()."; + return; + } + + indent_.resize(indent_.size() - 2); +} + +void Printer::Write(const char* data, int size) { + if (failed_) return; + if (size == 0) return; + + if (at_start_of_line_) { + // Insert an indent. + at_start_of_line_ = false; + Write(indent_.data(), indent_.size()); + if (failed_) return; + } + + while (size > buffer_size_) { + // Data exceeds space in the buffer. Copy what we can and request a + // new buffer. + memcpy(buffer_, data, buffer_size_); + data += buffer_size_; + size -= buffer_size_; + void* void_buffer; + failed_ = !output_->Next(&void_buffer, &buffer_size_); + if (failed_) return; + buffer_ = reinterpret_cast(void_buffer); + } + + // Buffer is big enough to receive the data; copy it. + memcpy(buffer_, data, size); + buffer_ += size; + buffer_size_ -= size; +} + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h new file mode 100644 index 00000000..ee8f46c8 --- /dev/null +++ b/src/google/protobuf/io/printer.h @@ -0,0 +1,109 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Utility class for writing text to a ZeroCopyOutputStream. + +#ifndef GOOGLE_PROTOBUF_IO_PRINTER_H__ +#define GOOGLE_PROTOBUF_IO_PRINTER_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +class ZeroCopyOutputStream; // zero_copy_stream.h + +// This simple utility class assists in code generation. It basically +// allows the caller to define a set of variables and then output some +// text with variable substitutions. Example usage: +// +// Printer printer(output, '$'); +// map vars; +// vars["name"] = "Bob"; +// printer.Print(vars, "My name is $name$."); +// +// The above writes "My name is Bob." to the output stream. +// +// Printer aggressively enforces correct usage, crashing (with assert failures) +// in the case of undefined variables. This helps greatly in debugging code +// which uses it. This class is not intended to be used by production servers. +class LIBPROTOBUF_EXPORT Printer { + public: + // Create a printer that writes text to the given output stream. Use the + // given character as the delimiter for variables. + Printer(ZeroCopyOutputStream* output, char variable_delimiter); + ~Printer(); + + // Print some text after applying variable substitutions. If a particular + // variable in the text is not defined, this will crash. Variables to be + // substituted are identified by their names surrounded by delimiter + // characters (as given to the constructor). The variable bindings are + // defined by the given map. + void Print(const map& variables, const char* text); + + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text); + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable, const string& value); + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable1, const string& value1, + const char* variable2, const string& value2); + // TODO(kenton): Overloaded versions with more variables? Two seems + // to be enough. + + // Indent text by two spaces. After calling Indent(), two spaces will be + // inserted at the beginning of each line of text. Indent() may be called + // multiple times to produce deeper indents. + void Indent(); + + // Reduces the current indent level by two spaces, or crashes if the indent + // level is zero. + void Outdent(); + + // True if any write to the underlying stream failed. (We don't just + // crash in this case because this is an I/O failure, not a programming + // error.) + bool failed() const { return failed_; } + + private: + // Write some text to the output buffer. + void Write(const char* data, int size); + + const char variable_delimiter_; + + ZeroCopyOutputStream* const output_; + char* buffer_; + int buffer_size_; + + string indent_; + bool at_start_of_line_; + bool failed_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Printer); +}; + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_PRINTER_H__ diff --git a/src/google/protobuf/io/printer_unittest.cc b/src/google/protobuf/io/printer_unittest.cc new file mode 100644 index 00000000..652728bc --- /dev/null +++ b/src/google/protobuf/io/printer_unittest.cc @@ -0,0 +1,210 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// Each test repeats over several block sizes in order to test both cases +// where particular writes cross a buffer boundary and cases where they do +// not. + +TEST(Printer, EmptyPrinter) { + char buffer[8192]; + const int block_size = 100; + ArrayOutputStream output(buffer, GOOGLE_ARRAYSIZE(buffer), block_size); + Printer printer(&output, '\0'); + EXPECT_TRUE(!printer.failed()); +} + +TEST(Printer, BasicPrinting) { + char buffer[8192]; + + for (int block_size = 1; block_size < 512; block_size *= 2) { + ArrayOutputStream output(buffer, sizeof(buffer), block_size); + + { + Printer printer(&output, '\0'); + + printer.Print("Hello World!"); + printer.Print(" This is the same line.\n"); + printer.Print("But this is a new one.\nAnd this is another one."); + + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "Hello World! This is the same line.\n" + "But this is a new one.\n" + "And this is another one."); + } +} + +TEST(Printer, VariableSubstitution) { + char buffer[8192]; + + for (int block_size = 1; block_size < 512; block_size *= 2) { + ArrayOutputStream output(buffer, sizeof(buffer), block_size); + + { + Printer printer(&output, '$'); + map vars; + + vars["foo"] = "World"; + vars["bar"] = "$foo$"; + vars["abcdefg"] = "1234"; + + printer.Print(vars, "Hello $foo$!\nbar = $bar$\n"); + printer.Print(vars, "$abcdefg$\nA literal dollar sign: $$"); + + vars["foo"] = "blah"; + printer.Print(vars, "\nNow foo = $foo$."); + + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "Hello World!\n" + "bar = $foo$\n" + "1234\n" + "A literal dollar sign: $\n" + "Now foo = blah."); + } +} + +TEST(Printer, InlineVariableSubstitution) { + char buffer[8192]; + + ArrayOutputStream output(buffer, sizeof(buffer)); + + { + Printer printer(&output, '$'); + printer.Print("Hello $foo$!\n", "foo", "World"); + printer.Print("$foo$ $bar$\n", "foo", "one", "bar", "two"); + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "Hello World!\n" + "one two\n"); +} + +TEST(Printer, Indenting) { + char buffer[8192]; + + for (int block_size = 1; block_size < 512; block_size *= 2) { + ArrayOutputStream output(buffer, sizeof(buffer), block_size); + + { + Printer printer(&output, '$'); + map vars; + + vars["newline"] = "\n"; + + printer.Print("This is not indented.\n"); + printer.Indent(); + printer.Print("This is indented\nAnd so is this\n"); + printer.Outdent(); + printer.Print("But this is not."); + printer.Indent(); + printer.Print(" And this is still the same line.\n" + "But this is indented.\n"); + printer.Print(vars, "Note that a newline in a variable will break " + "indenting, as we see$newline$here.\n"); + printer.Indent(); + printer.Print("And this"); + printer.Outdent(); + printer.Outdent(); + printer.Print(" is double-indented\nBack to normal."); + + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "This is not indented.\n" + " This is indented\n" + " And so is this\n" + "But this is not. And this is still the same line.\n" + " But this is indented.\n" + " Note that a newline in a variable will break indenting, as we see\n" + "here.\n" + " And this is double-indented\n" + "Back to normal."); + } +} + +// Death tests do not work on Windows as of yet. +#ifdef GTEST_HAS_DEATH_TEST +TEST(Printer, Death) { + char buffer[8192]; + + ArrayOutputStream output(buffer, sizeof(buffer)); + Printer printer(&output, '$'); + + EXPECT_DEBUG_DEATH(printer.Print("$nosuchvar$"), "Undefined variable"); + EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name"); + EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent"); +} +#endif // GTEST_HAS_DEATH_TEST + +TEST(Printer, WriteFailure) { + char buffer[16]; + + ArrayOutputStream output(buffer, sizeof(buffer)); + Printer printer(&output, '$'); + + // Print 16 bytes to fill the buffer exactly (should not fail). + printer.Print("0123456789abcdef"); + EXPECT_FALSE(printer.failed()); + + // Try to print one more byte (should fail). + printer.Print(" "); + EXPECT_TRUE(printer.failed()); + + // Should not crash + printer.Print("blah"); + EXPECT_TRUE(printer.failed()); + + // Buffer should contain the first 16 bytes written. + EXPECT_EQ("0123456789abcdef", string(buffer, sizeof(buffer))); +} + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc new file mode 100644 index 00000000..3864fcfb --- /dev/null +++ b/src/google/protobuf/io/tokenizer.cc @@ -0,0 +1,679 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Here we have a hand-written lexer. At first you might ask yourself, +// "Hand-written text processing? Is Kenton crazy?!" Well, first of all, +// yes I am crazy, but that's beside the point. There are actually reasons +// why I ended up writing this this way. +// +// The traditional approach to lexing is to use lex to generate a lexer for +// you. Unfortunately, lex's output is ridiculously ugly and difficult to +// integrate cleanly with C++ code, especially abstract code or code meant +// as a library. Better parser-generators exist but would add dependencies +// which most users won't already have, which we'd like to avoid. (GNU flex +// has a C++ output option, but it's still ridiculously ugly, non-abstract, +// and not library-friendly.) +// +// The next approach that any good software engineer should look at is to +// use regular expressions. And, indeed, I did. I have code which +// implements this same class using regular expressions. It's about 200 +// lines shorter. However: +// - Rather than error messages telling you "This string has an invalid +// escape sequence at line 5, column 45", you get error messages like +// "Parse error on line 5". Giving more precise errors requires adding +// a lot of code that ends up basically as complex as the hand-coded +// version anyway. +// - The regular expression to match a string literal looks like this: +// kString = new RE("(\"([^\"\\\\]|" // non-escaped +// "\\\\[abfnrtv?\"'\\\\0-7]|" // normal escape +// "\\\\x[0-9a-fA-F])*\"|" // hex escape +// "\'([^\'\\\\]|" // Also support single-quotes. +// "\\\\[abfnrtv?\"'\\\\0-7]|" +// "\\\\x[0-9a-fA-F])*\')"); +// Verifying the correctness of this line noise is actually harder than +// verifying the correctness of ConsumeString(), defined below. I'm not +// even confident that the above is correct, after staring at it for some +// time. +// - PCRE is fast, but there's still more overhead involved than the code +// below. +// - Sadly, regular expressions are not part of the C standard library, so +// using them would require depending on some other library. For the +// open source release, this could be really annoying. Nobody likes +// downloading one piece of software just to find that they need to +// download something else to make it work, and in all likelihood +// people downloading Protocol Buffers will already be doing so just +// to make something else work. We could include a copy of PCRE with +// our code, but that obligates us to keep it up-to-date and just seems +// like a big waste just to save 200 lines of code. +// +// On a similar but unrelated note, I'm even scared to use ctype.h. +// Apparently functions like isalpha() are locale-dependent. So, if we used +// that, then if this code is being called from some program that doesn't +// have its locale set to "C", it would behave strangely. We can't just set +// the locale to "C" ourselves since we might break the calling program that +// way, particularly if it is multi-threaded. WTF? Someone please let me +// (Kenton) know if I'm missing something here... +// +// I'd love to hear about other alternatives, though, as this code isn't +// exactly pretty. + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// As mentioned above, I don't trust ctype.h due to the presence of "locales". +// So, I have written replacement functions here. Someone please smack me if +// this is a bad idea or if there is some way around this. +// +// These "character classes" are designed to be used in template methods. +// For instance, Tokenizer::ConsumeZeroOrMore() will eat +// whitespace. + +// Note: No class is allowed to contain '\0', since this is used to mark end- +// of-input and is handled specially. + +#define CHARACTER_CLASS(NAME, EXPRESSION) \ + class NAME { \ + public: \ + static inline bool InClass(char c) { \ + return EXPRESSION; \ + } \ + } + +CHARACTER_CLASS(Whitespace, c == ' ' || c == '\n' || c == '\t' || + c == '\r' || c == '\v'); + +CHARACTER_CLASS(Unprintable, c < ' ' && c != '\0'); + +CHARACTER_CLASS(Digit, '0' <= c && c <= '9'); +CHARACTER_CLASS(OctalDigit, '0' <= c && c <= '7'); +CHARACTER_CLASS(HexDigit, ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F')); + +CHARACTER_CLASS(Letter, ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + (c == '_')); + +CHARACTER_CLASS(Alphanumeric, ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + (c == '_')); + +CHARACTER_CLASS(Escape, c == 'a' || c == 'b' || c == 'f' || c == 'n' || + c == 'r' || c == 't' || c == 'v' || c == '\\' || + c == '?' || c == '\'' || c == '\"'); + +#undef CHARACTER_CLASS + +// Given a char, interpret it as a numeric digit and return its value. +// This supports any number base up to 36. +inline int DigitValue(char digit) { + if ('0' <= digit && digit <= '9') return digit - '0'; + if ('a' <= digit && digit <= 'z') return digit - 'a' + 10; + if ('A' <= digit && digit <= 'Z') return digit - 'A' + 10; + return -1; +} + +// Inline because it's only used in one place. +inline char TranslateEscape(char c) { + switch (c) { + case 'a': return '\a'; + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case '\\': return '\\'; + case '?': return '\?'; // Trigraphs = :( + case '\'': return '\''; + case '"': return '\"'; + + // We expect escape sequences to have been validated separately. + default: return '?'; + } +} + +} // anonymous namespace + +ErrorCollector::~ErrorCollector() {} + +// =================================================================== + +Tokenizer::Tokenizer(ZeroCopyInputStream* input, + ErrorCollector* error_collector) + : input_(input), + error_collector_(error_collector), + buffer_(NULL), + buffer_size_(0), + buffer_pos_(0), + read_error_(false), + line_(0), + column_(0), + token_start_(-1), + allow_f_after_float_(false), + comment_style_(CPP_COMMENT_STYLE) { + + current_.line = 0; + current_.column = 0; + current_.type = TYPE_START; + + Refresh(); +} + +Tokenizer::~Tokenizer() { + // If we had any buffer left unread, return it to the underlying stream + // so that someone else can read it. + if (buffer_size_ > buffer_pos_) { + input_->BackUp(buffer_size_ - buffer_pos_); + } +} + +// ------------------------------------------------------------------- +// Internal helpers. + +void Tokenizer::NextChar() { + // Update our line and column counters based on the character being + // consumed. + if (current_char_ == '\n') { + ++line_; + column_ = 0; + } else if (current_char_ == '\t') { + column_ += kTabWidth - column_ % kTabWidth; + } else { + ++column_; + } + + // Advance to the next character. + ++buffer_pos_; + if (buffer_pos_ < buffer_size_) { + current_char_ = buffer_[buffer_pos_]; + } else { + Refresh(); + } +} + +void Tokenizer::Refresh() { + if (read_error_) { + current_char_ = '\0'; + return; + } + + // If we're in a token, append the rest of the buffer to it. + if (token_start_ >= 0 && token_start_ < buffer_size_) { + current_.text.append(buffer_ + token_start_, buffer_size_ - token_start_); + token_start_ = 0; + } + + const void* data = NULL; + buffer_ = NULL; + buffer_pos_ = 0; + do { + if (!input_->Next(&data, &buffer_size_)) { + // end of stream (or read error) + buffer_size_ = 0; + read_error_ = true; + current_char_ = '\0'; + return; + } + } while (buffer_size_ == 0); + + buffer_ = static_cast(data); + + current_char_ = buffer_[0]; +} + +inline void Tokenizer::StartToken() { + token_start_ = buffer_pos_; + current_.type = TYPE_START; // Just for the sake of initializing it. + current_.text.clear(); + current_.line = line_; + current_.column = column_; +} + +inline void Tokenizer::EndToken() { + // Note: The if() is necessary because some STL implementations crash when + // you call string::append(NULL, 0), presumably because they are trying to + // be helpful by detecting the NULL pointer, even though there's nothing + // wrong with reading zero bytes from NULL. + if (buffer_pos_ != token_start_) { + current_.text.append(buffer_ + token_start_, buffer_pos_ - token_start_); + } + token_start_ = -1; +} + +// ------------------------------------------------------------------- +// Helper methods that consume characters. + +template +inline bool Tokenizer::LookingAt() { + return CharacterClass::InClass(current_char_); +} + +template +inline bool Tokenizer::TryConsumeOne() { + if (CharacterClass::InClass(current_char_)) { + NextChar(); + return true; + } else { + return false; + } +} + +inline bool Tokenizer::TryConsume(char c) { + if (current_char_ == c) { + NextChar(); + return true; + } else { + return false; + } +} + +template +inline void Tokenizer::ConsumeZeroOrMore() { + while (CharacterClass::InClass(current_char_)) { + NextChar(); + } +} + +template +inline void Tokenizer::ConsumeOneOrMore(const char* error) { + if (!CharacterClass::InClass(current_char_)) { + AddError(error); + } else { + do { + NextChar(); + } while (CharacterClass::InClass(current_char_)); + } +} + +// ------------------------------------------------------------------- +// Methods that read whole patterns matching certain kinds of tokens +// or comments. + +void Tokenizer::ConsumeString(char delimiter) { + while (true) { + switch (current_char_) { + case '\0': + case '\n': { + AddError("String literals cannot cross line boundaries."); + return; + } + + case '\\': { + // An escape sequence. + NextChar(); + if (TryConsumeOne()) { + // Valid escape sequence. + } else if (TryConsumeOne()) { + // Possibly followed by two more octal digits, but these will + // just be consumed by the main loop anyway so we don't need + // to do so explicitly here. + } else if (TryConsume('x') || TryConsume('X')) { + if (!TryConsumeOne()) { + AddError("Expected hex digits for escape sequence."); + } + // Possibly followed by another hex digit, but again we don't care. + } else { + AddError("Invalid escape sequence in string literal."); + } + break; + } + + default: { + if (current_char_ == delimiter) { + NextChar(); + return; + } + NextChar(); + break; + } + } + } +} + +Tokenizer::TokenType Tokenizer::ConsumeNumber(bool started_with_zero, + bool started_with_dot) { + bool is_float = false; + + if (started_with_zero && (TryConsume('x') || TryConsume('X'))) { + // A hex number (started with "0x"). + ConsumeOneOrMore("\"0x\" must be followed by hex digits."); + + } else if (started_with_zero && LookingAt()) { + // An octal number (had a leading zero). + ConsumeZeroOrMore(); + if (LookingAt()) { + AddError("Numbers starting with leading zero must be in octal."); + ConsumeZeroOrMore(); + } + + } else { + // A decimal number. + if (started_with_dot) { + is_float = true; + ConsumeZeroOrMore(); + } else { + ConsumeZeroOrMore(); + + if (TryConsume('.')) { + is_float = true; + ConsumeZeroOrMore(); + } + } + + if (TryConsume('e') || TryConsume('E')) { + is_float = true; + TryConsume('-') || TryConsume('+'); + ConsumeOneOrMore("\"e\" must be followed by exponent."); + } + + if (allow_f_after_float_ && (TryConsume('f') || TryConsume('F'))) { + is_float = true; + } + } + + if (LookingAt()) { + AddError("Need space between number and identifier."); + } else if (current_char_ == '.') { + if (is_float) { + AddError( + "Already saw decimal point or exponent; can't have another one."); + } else { + AddError("Hex and octal numbers must be integers."); + } + } + + return is_float ? TYPE_FLOAT : TYPE_INTEGER; +} + +void Tokenizer::ConsumeLineComment() { + while (current_char_ != '\0' && current_char_ != '\n') { + NextChar(); + } + TryConsume('\n'); +} + +void Tokenizer::ConsumeBlockComment() { + int start_line = line_; + int start_column = column_ - 2; + + while (true) { + while (current_char_ != '\0' && + current_char_ != '*' && + current_char_ != '/') { + NextChar(); + } + + if (TryConsume('*') && TryConsume('/')) { + // End of comment. + break; + } else if (TryConsume('/') && current_char_ == '*') { + // Note: We didn't consume the '*' because if there is a '/' after it + // we want to interpret that as the end of the comment. + AddError( + "\"/*\" inside block comment. Block comments cannot be nested."); + } else if (current_char_ == '\0') { + AddError("End-of-file inside block comment."); + error_collector_->AddError( + start_line, start_column, " Comment started here."); + break; + } + } +} + +// ------------------------------------------------------------------- + +bool Tokenizer::Next() { + TokenType last_token_type = current_.type; + + // Did we skip any characters after the last token? + bool skipped_stuff = false; + + while (!read_error_) { + if (TryConsumeOne()) { + ConsumeZeroOrMore(); + + } else if (comment_style_ == CPP_COMMENT_STYLE && TryConsume('/')) { + // Starting a comment? + if (TryConsume('/')) { + ConsumeLineComment(); + } else if (TryConsume('*')) { + ConsumeBlockComment(); + } else { + // Oops, it was just a slash. Return it. + current_.type = TYPE_SYMBOL; + current_.text = "/"; + current_.line = line_; + current_.column = column_ - 1; + return true; + } + + } else if (comment_style_ == SH_COMMENT_STYLE && TryConsume('#')) { + ConsumeLineComment(); + + } else if (LookingAt() || current_char_ == '\0') { + AddError("Invalid control characters encountered in text."); + NextChar(); + // Skip more unprintable characters, too. But, remember that '\0' is + // also what current_char_ is set to after EOF / read error. We have + // to be careful not to go into an infinite loop of trying to consume + // it, so make sure to check read_error_ explicitly before consuming + // '\0'. + while (TryConsumeOne() || + (!read_error_ && TryConsume('\0'))) { + // Ignore. + } + + } else { + // Reading some sort of token. + StartToken(); + + if (TryConsumeOne()) { + ConsumeZeroOrMore(); + current_.type = TYPE_IDENTIFIER; + } else if (TryConsume('0')) { + current_.type = ConsumeNumber(true, false); + } else if (TryConsume('.')) { + // This could be the beginning of a floating-point number, or it could + // just be a '.' symbol. + + if (TryConsumeOne()) { + // It's a floating-point number. + if (last_token_type == TYPE_IDENTIFIER && !skipped_stuff) { + // We don't accept syntax like "blah.123". + error_collector_->AddError(line_, column_ - 2, + "Need space between identifier and decimal point."); + } + current_.type = ConsumeNumber(false, true); + } else { + current_.type = TYPE_SYMBOL; + } + } else if (TryConsumeOne()) { + current_.type = ConsumeNumber(false, false); + } else if (TryConsume('\"')) { + ConsumeString('\"'); + current_.type = TYPE_STRING; + } else if (TryConsume('\'')) { + ConsumeString('\''); + current_.type = TYPE_STRING; + } else { + NextChar(); + current_.type = TYPE_SYMBOL; + } + + EndToken(); + return true; + } + + skipped_stuff = true; + } + + // EOF + current_.type = TYPE_END; + current_.text.clear(); + current_.line = line_; + current_.column = column_; + return false; +} + +// ------------------------------------------------------------------- +// Token-parsing helpers. Remember that these don't need to report +// errors since any errors should already have been reported while +// tokenizing. Also, these can assume that whatever text they +// are given is text that the tokenizer actually parsed as a token +// of the given type. + +bool Tokenizer::ParseInteger(const string& text, uint64 max_value, + uint64* output) { + // Sadly, we can't just use strtoul() since it is only 32-bit and strtoull() + // is non-standard. I hate the C standard library. :( + +// return strtoull(text.c_str(), NULL, 0); + + const char* ptr = text.c_str(); + int base = 10; + if (ptr[0] == '0') { + if (ptr[1] == 'x') { + // This is hex. + base = 16; + ptr += 2; + } else { + // This is octal. + base = 8; + } + } + + uint64 result = 0; + for (; *ptr != '\0'; ptr++) { + int digit = DigitValue(*ptr); + GOOGLE_LOG_IF(DFATAL, digit < 0 || digit >= base) + << " Tokenizer::ParseInteger() passed text that could not have been" + " tokenized as an integer: " << CEscape(text); + if (digit > max_value || result > (max_value - digit) / base) { + // Overflow. + return false; + } + result = result * base + digit; + } + + *output = result; + return true; +} + +double Tokenizer::ParseFloat(const string& text) { + const char* start = text.c_str(); + char* end; + double result = NoLocaleStrtod(start, &end); + + // "1e" is not a valid float, but if the tokenizer reads it, it will + // report an error but still return it as a valid token. We need to + // accept anything the tokenizer could possibly return, error or not. + if (*end == 'e' || *end == 'E') { + ++end; + if (*end == '-' || *end == '+') ++end; + } + + // If the Tokenizer had allow_f_after_float_ enabled, the float may be + // suffixed with the letter 'f'. + if (*end == 'f' || *end == 'F') { + ++end; + } + + GOOGLE_LOG_IF(DFATAL, end - start != text.size() || *start == '-') + << " Tokenizer::ParseFloat() passed text that could not have been" + " tokenized as a float: " << CEscape(text); + return result; +} + +void Tokenizer::ParseString(const string& text, string* output) { + output->clear(); + + // Reminder: text[0] is always the quote character. (If text is + // empty, it's invalid, so we'll just return.) + if (text.empty()) { + GOOGLE_LOG(DFATAL) + << " ParseString::ParseString() passed text that could not have been" + " tokenized as a string: " << CEscape(text); + return; + } + + output->reserve(text.size()); + + // Loop through the string copying characters to "output" and + // interpreting escape sequences. Note that any invalid escape + // sequences or other errors were already reported while tokenizing. + // In this case we do not need to produce valid results. + for (const char* ptr = text.c_str() + 1; *ptr != '\0'; ptr++) { + if (*ptr == '\\' && ptr[1] != '\0') { + // An escape sequence. + ++ptr; + + if (OctalDigit::InClass(*ptr)) { + // An octal escape. May one, two, or three digits. + int code = DigitValue(*ptr); + if (OctalDigit::InClass(ptr[1])) { + ++ptr; + code = code * 8 + DigitValue(*ptr); + } + if (OctalDigit::InClass(ptr[1])) { + ++ptr; + code = code * 8 + DigitValue(*ptr); + } + output->push_back(static_cast(code)); + + } else if (*ptr == 'x') { + // A hex escape. May zero, one, or two digits. (The zero case + // will have been caught as an error earlier.) + int code = 0; + if (HexDigit::InClass(ptr[1])) { + ++ptr; + code = DigitValue(*ptr); + } + if (HexDigit::InClass(ptr[1])) { + ++ptr; + code = code * 16 + DigitValue(*ptr); + } + output->push_back(static_cast(code)); + + } else { + // Some other escape code. + output->push_back(TranslateEscape(*ptr)); + } + + } else if (*ptr == text[0]) { + // Ignore quote matching the starting quote. + } else { + output->push_back(*ptr); + } + } + + return; +} + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/tokenizer.h b/src/google/protobuf/io/tokenizer.h new file mode 100644 index 00000000..841564ce --- /dev/null +++ b/src/google/protobuf/io/tokenizer.h @@ -0,0 +1,276 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Class for parsing tokenized text from a ZeroCopyInputStream. + +#ifndef GOOGLE_PROTOBUF_IO_TOKENIZER_H__ +#define GOOGLE_PROTOBUF_IO_TOKENIZER_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +class ZeroCopyInputStream; // zero_copy_stream.h + +// Defined in this file. +class ErrorCollector; +class Tokenizer; + +// Abstract interface for an object which collects the errors that occur +// during parsing. A typical implementation might simply print the errors +// to stdout. +class LIBPROTOBUF_EXPORT ErrorCollector { + public: + inline ErrorCollector() {} + virtual ~ErrorCollector(); + + // Indicates that there was an error in the input at the given line and + // column numbers. The numbers are zero-based, so you may want to add + // 1 to each before printing them. + virtual void AddError(int line, int column, const string& message) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector); +}; + +// This class converts a stream of raw text into a stream of tokens for +// the protocol definition parser to parse. The tokens recognized are +// similar to those that make up the C language; see the TokenType enum for +// precise descriptions. Whitespace and comments are skipped. By default, +// C- and C++-style comments are recognized, but other styles can be used by +// calling set_comment_style(). +class LIBPROTOBUF_EXPORT Tokenizer { + public: + // Construct a Tokenizer that reads and tokenizes text from the given + // input stream and writes errors to the given error_collector. + // The caller keeps ownership of input and error_collector. + Tokenizer(ZeroCopyInputStream* input, ErrorCollector* error_collector); + ~Tokenizer(); + + enum TokenType { + TYPE_START, // Next() has not yet been called. + TYPE_END, // End of input reached. "text" is empty. + + TYPE_IDENTIFIER, // A sequence of letters, digits, and underscores, not + // starting with a digit. It is an error for a number + // to be followed by an identifier with no space in + // between. + TYPE_INTEGER, // A sequence of digits representing an integer. Normally + // the digits are decimal, but a prefix of "0x" indicates + // a hex number and a leading zero indicates octal, just + // like with C numeric literals. A leading negative sign + // is NOT included in the token; it's up to the parser to + // interpret the unary minus operator on its own. + TYPE_FLOAT, // A floating point literal, with a fractional part and/or + // an exponent. Always in decimal. Again, never + // negative. + TYPE_STRING, // A quoted sequence of escaped characters. Either single + // or double quotes can be used, but they must match. + // A string literal cannot cross a line break. + TYPE_SYMBOL, // Any other printable character, like '!' or '+'. + // Symbols are always a single character, so "!+$%" is + // four tokens. + }; + + // Structure representing a token read from the token stream. + struct Token { + TokenType type; + string text; // The exact text of the token as it appeared in + // the input. e.g. tokens of TYPE_STRING will still + // be escaped and in quotes. + + // "line" and "column" specify the position of the first character of + // the token within the input stream. They are zero-based. + int line; + int column; + }; + + // Get the current token. This is updated when Next() is called. Before + // the first call to Next(), current() has type TYPE_START and no contents. + const Token& current(); + + // Advance to the next token. Returns false if the end of the input is + // reached. + bool Next(); + + // Parse helpers --------------------------------------------------- + + // Parses a TYPE_FLOAT token. This never fails, so long as the text actually + // comes from a TYPE_FLOAT token parsed by Tokenizer. If it doesn't, the + // result is undefined (possibly an assert failure). + static double ParseFloat(const string& text); + + // Parses a TYPE_STRING token. This never fails, so long as the text actually + // comes from a TYPE_STRING token parsed by Tokenizer. If it doesn't, the + // result is undefined (possibly an assert failure). + static void ParseString(const string& text, string* output); + + // Parses a TYPE_INTEGER token. Returns false if the result would be + // greater than max_value. Otherwise, returns true and sets *output to the + // result. If the text is not from a Token of type TYPE_INTEGER originally + // parsed by a Tokenizer, the result is undefined (possibly an assert + // failure). + static bool ParseInteger(const string& text, uint64 max_value, + uint64* output); + + // Options --------------------------------------------------------- + + // Set true to allow floats to be suffixed with the letter 'f'. Tokens + // which would otherwise be integers but which have the 'f' suffix will be + // forced to be interpreted as floats. For all other purposes, the 'f' is + // ignored. + void set_allow_f_after_float(bool value) { allow_f_after_float_ = value; } + + // Valid values for set_comment_style(). + enum CommentStyle { + // Line comments begin with "//", block comments are delimited by "/*" and + // "*/". + CPP_COMMENT_STYLE, + // Line comments begin with "#". No way to write block comments. + SH_COMMENT_STYLE + }; + + // Sets the comment style. + void set_comment_style(CommentStyle style) { comment_style_ = style; } + + // ----------------------------------------------------------------- + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Tokenizer); + + Token current_; // Returned by current(). + + ZeroCopyInputStream* input_; + ErrorCollector* error_collector_; + + char current_char_; // == buffer_[buffer_pos_], updated by NextChar(). + const char* buffer_; // Current buffer returned from input_. + int buffer_size_; // Size of buffer_. + int buffer_pos_; // Current position within the buffer. + bool read_error_; // Did we previously encounter a read error? + + // Line and column number of current_char_ within the whole input stream. + int line_; + int column_; + + // Position in buffer_ where StartToken() was called. If the token + // started in the previous buffer, this is zero, and current_.text already + // contains the part of the token from the previous buffer. If not + // currently parsing a token, this is -1. + int token_start_; + + // Options. + bool allow_f_after_float_; + CommentStyle comment_style_; + + // Since we count columns we need to interpret tabs somehow. We'll take + // the standard 8-character definition for lack of any way to do better. + static const int kTabWidth = 8; + + // ----------------------------------------------------------------- + // Helper methods. + + // Consume this character and advance to the next one. + void NextChar(); + + // Read a new buffer from the input. + void Refresh(); + + // Called when the current character is the first character of a new + // token (not including whitespace or comments). + inline void StartToken(); + // Called when the current character is the first character after the + // end of the last token. After this returns, current_.text will + // contain all text consumed since StartToken() was called. + inline void EndToken(); + + // Convenience method to add an error at the current line and column. + void AddError(const string& message) { + error_collector_->AddError(line_, column_, message); + } + + // ----------------------------------------------------------------- + // The following four methods are used to consume tokens of specific + // types. They are actually used to consume all characters *after* + // the first, since the calling function consumes the first character + // in order to decide what kind of token is being read. + + // Read and consume a string, ending when the given delimiter is + // consumed. + void ConsumeString(char delimiter); + + // Read and consume a number, returning TYPE_FLOAT or TYPE_INTEGER + // depending on what was read. This needs to know if the first + // character was a zero in order to correctly recognize hex and octal + // numbers. + // It also needs to know if the first characted was a . to parse floating + // point correctly. + TokenType ConsumeNumber(bool started_with_zero, bool started_with_dot); + + // Consume the rest of a line. + void ConsumeLineComment(); + // Consume until "*/". + void ConsumeBlockComment(); + + // ----------------------------------------------------------------- + // These helper methods make the parsing code more readable. The + // "character classes" refered to are defined at the top of the .cc file. + // Basically it is a C++ class with one method: + // static bool InClass(char c); + // The method returns true if c is a member of this "class", like "Letter" + // or "Digit". + + // Returns true if the current character is of the given character + // class, but does not consume anything. + template + inline bool LookingAt(); + + // If the current character is in the given class, consume it and return + // true. Otherwise return false. + // e.g. TryConsumeOne() + template + inline bool TryConsumeOne(); + + // Like above, but try to consume the specific character indicated. + inline bool TryConsume(char c); + + // Consume zero or more of the given character class. + template + inline void ConsumeZeroOrMore(); + + // Consume one or more of the given character class or log the given + // error message. + // e.g. ConsumeOneOrMore("Expected digits."); + template + inline void ConsumeOneOrMore(const char* error); +}; + +// inline methods ==================================================== +inline const Tokenizer::Token& Tokenizer::current() { + return current_; +} + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_TOKENIZER_H__ diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc new file mode 100644 index 00000000..e2cede7e --- /dev/null +++ b/src/google/protobuf/io/tokenizer_unittest.cc @@ -0,0 +1,706 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// =================================================================== +// Data-Driven Test Infrastructure + +// TODO(kenton): This is copied from coded_stream_unittest. This is +// temporary until these fetaures are integrated into gTest itself. + +// TEST_1D and TEST_2D are macros I'd eventually like to see added to +// gTest. These macros can be used to declare tests which should be +// run multiple times, once for each item in some input array. TEST_1D +// tests all cases in a single input array. TEST_2D tests all +// combinations of cases from two arrays. The arrays must be statically +// defined such that the GOOGLE_ARRAYSIZE() macro works on them. Example: +// +// int kCases[] = {1, 2, 3, 4} +// TEST_1D(MyFixture, MyTest, kCases) { +// EXPECT_GT(kCases_case, 0); +// } +// +// This test iterates through the numbers 1, 2, 3, and 4 and tests that +// they are all grater than zero. In case of failure, the exact case +// which failed will be printed. The case type must be printable using +// ostream::operator<<. + +#define TEST_1D(FIXTURE, NAME, CASES) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType& CASES##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES); i++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES " case #" << i << ": " << CASES[i]); \ + DoSingleCase(CASES[i]); \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType& CASES##_case) + +#define TEST_2D(FIXTURE, NAME, CASES1, CASES2) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES1); i++) { \ + for (int j = 0; j < GOOGLE_ARRAYSIZE(CASES2); j++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES1 " case #" << i << ": " << CASES1[i] << ", " \ + << #CASES2 " case #" << j << ": " << CASES2[j]); \ + DoSingleCase(CASES1[i], CASES2[j]); \ + } \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case) + +// ------------------------------------------------------------------- + +// An input stream that is basically like an ArrayInputStream but sometimes +// returns empty buffers, just to throw us off. +class TestInputStream : public ZeroCopyInputStream { + public: + TestInputStream(const void* data, int size, int block_size) + : array_stream_(data, size, block_size), counter_(0) {} + ~TestInputStream() {} + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size) { + // We'll return empty buffers starting with the first buffer, and every + // 3 and 5 buffers after that. + if (counter_ % 3 == 0 || counter_ % 5 == 0) { + *data = NULL; + *size = 0; + ++counter_; + return true; + } else { + ++counter_; + return array_stream_.Next(data, size); + } + } + + void BackUp(int count) { return array_stream_.BackUp(count); } + bool Skip(int count) { return array_stream_.Skip(count); } + int64 ByteCount() const { return array_stream_.ByteCount(); } + + private: + ArrayInputStream array_stream_; + int counter_; +}; + +// ------------------------------------------------------------------- + +// An error collector which simply concatenates all its errors into a big +// block of text which can be checked. +class TestErrorCollector : public ErrorCollector { + public: + TestErrorCollector() {} + ~TestErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(int line, int column, const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1: $2\n", + line, column, message); + } +}; + +// ------------------------------------------------------------------- + +// We test each operation over a variety of block sizes to insure that +// we test cases where reads cross buffer boundaries as well as cases +// where they don't. This is sort of a brute-force approach to this, +// but it's easy to write and easy to understand. +const int kBlockSizes[] = {1, 2, 3, 5, 7, 13, 32, 1024}; + +class TokenizerTest : public testing::Test { + protected: + // For easy testing. + uint64 ParseInteger(const string& text) { + uint64 result; + EXPECT_TRUE(Tokenizer::ParseInteger(text, kuint64max, &result)); + return result; + } +}; + +// =================================================================== + +// These tests causes gcc 3.3.5 (and earlier?) to give the cryptic error: +// "sorry, unimplemented: `method_call_expr' not supported by dump_expr" +#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) + +// In each test case, the entire input text should parse as a single token +// of the given type. +struct SimpleTokenCase { + string input; + Tokenizer::TokenType type; +}; + +inline ostream& operator<<(ostream& out, + const SimpleTokenCase& test_case) { + return out << CEscape(test_case.input); +} + +SimpleTokenCase kSimpleTokenCases[] = { + // Test identifiers. + { "hello", Tokenizer::TYPE_IDENTIFIER }, + + // Test integers. + { "123", Tokenizer::TYPE_INTEGER }, + { "0xab6", Tokenizer::TYPE_INTEGER }, + { "0XAB6", Tokenizer::TYPE_INTEGER }, + { "0X1234567", Tokenizer::TYPE_INTEGER }, + { "0x89abcdef", Tokenizer::TYPE_INTEGER }, + { "0x89ABCDEF", Tokenizer::TYPE_INTEGER }, + { "01234567", Tokenizer::TYPE_INTEGER }, + + // Test floats. + { "123.45", Tokenizer::TYPE_FLOAT }, + { "1.", Tokenizer::TYPE_FLOAT }, + { "1e3", Tokenizer::TYPE_FLOAT }, + { "1E3", Tokenizer::TYPE_FLOAT }, + { "1e-3", Tokenizer::TYPE_FLOAT }, + { "1e+3", Tokenizer::TYPE_FLOAT }, + { "1.e3", Tokenizer::TYPE_FLOAT }, + { "1.2e3", Tokenizer::TYPE_FLOAT }, + { ".1", Tokenizer::TYPE_FLOAT }, + { ".1e3", Tokenizer::TYPE_FLOAT }, + { ".1e-3", Tokenizer::TYPE_FLOAT }, + { ".1e+3", Tokenizer::TYPE_FLOAT }, + + // Test strings. + { "'hello'", Tokenizer::TYPE_STRING }, + { "\"foo\"", Tokenizer::TYPE_STRING }, + { "'a\"b'", Tokenizer::TYPE_STRING }, + { "\"a'b\"", Tokenizer::TYPE_STRING }, + { "'a\\'b'", Tokenizer::TYPE_STRING }, + { "\"a\\\"b\"", Tokenizer::TYPE_STRING }, + { "'\\xf'", Tokenizer::TYPE_STRING }, + { "'\\0'", Tokenizer::TYPE_STRING }, + + // Test symbols. + { "+", Tokenizer::TYPE_SYMBOL }, + { ".", Tokenizer::TYPE_SYMBOL }, +}; + +TEST_2D(TokenizerTest, SimpleTokens, kSimpleTokenCases, kBlockSizes) { + // Set up the tokenizer. + TestInputStream input(kSimpleTokenCases_case.input.data(), + kSimpleTokenCases_case.input.size(), + kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + // Before Next() is called, the initial token should always be TYPE_START. + EXPECT_EQ(Tokenizer::TYPE_START, tokenizer.current().type); + EXPECT_EQ("", tokenizer.current().text); + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(0, tokenizer.current().column); + + // Parse the token. + ASSERT_TRUE(tokenizer.Next()); + + // Check that it has the right type. + EXPECT_EQ(kSimpleTokenCases_case.type, tokenizer.current().type); + // Check that it contains the complete input text. + EXPECT_EQ(kSimpleTokenCases_case.input, tokenizer.current().text); + // Check that it is located at the beginning of the input + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(0, tokenizer.current().column); + + // There should be no more input. + EXPECT_FALSE(tokenizer.Next()); + + // After Next() returns false, the token should have type TYPE_END. + EXPECT_EQ(Tokenizer::TYPE_END, tokenizer.current().type); + EXPECT_EQ("", tokenizer.current().text); + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(kSimpleTokenCases_case.input.size(), tokenizer.current().column); + + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +TEST_1D(TokenizerTest, FloatSuffix, kBlockSizes) { + // Test the "allow_f_after_float" option. + + // Set up the tokenizer. + const char* text = "1f 2.5f 6e3f 7F"; + TestInputStream input(text, strlen(text), kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + tokenizer.set_allow_f_after_float(true); + + // Advance through tokens and check that they are parsed as expected. + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "1f"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "2.5f"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "6e3f"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "7F"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + + // There should be no more input. + EXPECT_FALSE(tokenizer.Next()); + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +#endif + +// ------------------------------------------------------------------- + +// In each case, the input is parsed to produce a list of tokens. The +// last token in "output" must have type TYPE_END. +struct MultiTokenCase { + string input; + Tokenizer::Token output[10]; // The compiler wants a constant array + // size for initialization to work. There + // is no reason this can't be increased if + // needed. +}; + +inline ostream& operator<<(ostream& out, + const MultiTokenCase& test_case) { + return out << CEscape(test_case.input); +} + +MultiTokenCase kMultiTokenCases[] = { + // Test empty input. + { "", { + { Tokenizer::TYPE_END , "" , 0, 0 }, + }}, + + // Test all token types at the same time. + { "foo 1 1.2 + 'bar'", { + { Tokenizer::TYPE_IDENTIFIER, "foo" , 0, 0 }, + { Tokenizer::TYPE_INTEGER , "1" , 0, 4 }, + { Tokenizer::TYPE_FLOAT , "1.2" , 0, 6 }, + { Tokenizer::TYPE_SYMBOL , "+" , 0, 10 }, + { Tokenizer::TYPE_STRING , "'bar'", 0, 12 }, + { Tokenizer::TYPE_END , "" , 0, 17 }, + }}, + + // Test that consecutive symbols are parsed as separate tokens. + { "!@+%", { + { Tokenizer::TYPE_SYMBOL , "!" , 0, 0 }, + { Tokenizer::TYPE_SYMBOL , "@" , 0, 1 }, + { Tokenizer::TYPE_SYMBOL , "+" , 0, 2 }, + { Tokenizer::TYPE_SYMBOL , "%" , 0, 3 }, + { Tokenizer::TYPE_END , "" , 0, 4 }, + }}, + + // Test that newlines affect line numbers correctly. + { "foo bar\nrab oof", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 4 }, + { Tokenizer::TYPE_IDENTIFIER, "rab", 1, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "oof", 1, 4 }, + { Tokenizer::TYPE_END , "" , 1, 7 }, + }}, + + // Test that tabs affect column numbers correctly. + { "foo\tbar \tbaz", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 8 }, + { Tokenizer::TYPE_IDENTIFIER, "baz", 0, 16 }, + { Tokenizer::TYPE_END , "" , 0, 19 }, + }}, + + // Test that line comments are ignored. + { "foo // This is a comment\n" + "bar // This is another comment", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 1, 0 }, + { Tokenizer::TYPE_END , "" , 1, 30 }, + }}, + + // Test that block comments are ignored. + { "foo /* This is a block comment */ bar", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 34 }, + { Tokenizer::TYPE_END , "" , 0, 37 }, + }}, + + // Test that sh-style comments are not ignored by default. + { "foo # bar\n" + "baz", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_SYMBOL , "#" , 0, 4 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 6 }, + { Tokenizer::TYPE_IDENTIFIER, "baz", 1, 0 }, + { Tokenizer::TYPE_END , "" , 1, 3 }, + }}, +}; + +TEST_2D(TokenizerTest, MultipleTokens, kMultiTokenCases, kBlockSizes) { + // Set up the tokenizer. + TestInputStream input(kMultiTokenCases_case.input.data(), + kMultiTokenCases_case.input.size(), + kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + // Before Next() is called, the initial token should always be TYPE_START. + EXPECT_EQ(Tokenizer::TYPE_START, tokenizer.current().type); + EXPECT_EQ("", tokenizer.current().text); + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(0, tokenizer.current().column); + + // Loop through all expected tokens. + int i = 0; + Tokenizer::Token token; + do { + token = kMultiTokenCases_case.output[i++]; + + SCOPED_TRACE(testing::Message() << "Token #" << i << ": " << token.text); + + // Next() should only return false when it hits the end token. + if (token.type != Tokenizer::TYPE_END) { + ASSERT_TRUE(tokenizer.Next()); + } else { + ASSERT_FALSE(tokenizer.Next()); + } + + // Check that the token matches the expected one. + EXPECT_EQ(token.type, tokenizer.current().type); + EXPECT_EQ(token.text, tokenizer.current().text); + EXPECT_EQ(token.line, tokenizer.current().line); + EXPECT_EQ(token.column, tokenizer.current().column); + + } while (token.type != Tokenizer::TYPE_END); + + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +// This test causes gcc 3.3.5 (and earlier?) to give the cryptic error: +// "sorry, unimplemented: `method_call_expr' not supported by dump_expr" +#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) + +TEST_1D(TokenizerTest, ShCommentStyle, kBlockSizes) { + // Test the "comment_style" option. + + const char* text = "foo # bar\n" + "baz // qux\n" + "corge /* grault */\n" + "garply"; + const char* const kTokens[] = {"foo", // "# bar" is ignored + "baz", "/", "/", "qux", + "corge", "/", "*", "grault", "*", "/", + "garply"}; + + // Set up the tokenizer. + TestInputStream input(text, strlen(text), kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + tokenizer.set_comment_style(Tokenizer::SH_COMMENT_STYLE); + + // Advance through tokens and check that they are parsed as expected. + for (int i = 0; i < GOOGLE_ARRAYSIZE(kTokens); i++) { + EXPECT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, kTokens[i]); + } + + // There should be no more input. + EXPECT_FALSE(tokenizer.Next()); + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +#endif + +// ------------------------------------------------------------------- + +// Test parse helpers. It's not really worth setting up a full data-driven +// test here. +TEST_F(TokenizerTest, ParseInteger) { + EXPECT_EQ(0, ParseInteger("0")); + EXPECT_EQ(123, ParseInteger("123")); + EXPECT_EQ(0xabcdef12u, ParseInteger("0xabcdef12")); + EXPECT_EQ(0xabcdef12u, ParseInteger("0xABCDEF12")); + EXPECT_EQ(kuint64max, ParseInteger("0xFFFFFFFFFFFFFFFF")); + EXPECT_EQ(01234567, ParseInteger("01234567")); + + // Test invalid integers that may still be tokenized as integers. + EXPECT_EQ(0, ParseInteger("0x")); + +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + // Test invalid integers that will never be tokenized as integers. + EXPECT_DEBUG_DEATH(ParseInteger("zxy"), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(ParseInteger("1.2"), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(ParseInteger("08"), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(ParseInteger("0xg"), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(ParseInteger("-1"), + "passed text that could not have been tokenized as an integer"); +#endif // GTEST_HAS_DEATH_TEST + + // Test overflows. + uint64 i; + EXPECT_TRUE (Tokenizer::ParseInteger("0", 0, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("1", 0, &i)); + EXPECT_TRUE (Tokenizer::ParseInteger("1", 1, &i)); + EXPECT_TRUE (Tokenizer::ParseInteger("12345", 12345, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("12346", 12345, &i)); + EXPECT_TRUE (Tokenizer::ParseInteger("0xFFFFFFFFFFFFFFFF" , kuint64max, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("0x10000000000000000", kuint64max, &i)); +} + +TEST_F(TokenizerTest, ParseFloat) { + EXPECT_DOUBLE_EQ(1 , Tokenizer::ParseFloat("1.")); + EXPECT_DOUBLE_EQ(1e3 , Tokenizer::ParseFloat("1e3")); + EXPECT_DOUBLE_EQ(1e3 , Tokenizer::ParseFloat("1E3")); + EXPECT_DOUBLE_EQ(1.5e3, Tokenizer::ParseFloat("1.5e3")); + EXPECT_DOUBLE_EQ(.1 , Tokenizer::ParseFloat(".1")); + EXPECT_DOUBLE_EQ(.25 , Tokenizer::ParseFloat(".25")); + EXPECT_DOUBLE_EQ(.1e3 , Tokenizer::ParseFloat(".1e3")); + EXPECT_DOUBLE_EQ(.25e3, Tokenizer::ParseFloat(".25e3")); + EXPECT_DOUBLE_EQ(.1e+3, Tokenizer::ParseFloat(".1e+3")); + EXPECT_DOUBLE_EQ(.1e-3, Tokenizer::ParseFloat(".1e-3")); + EXPECT_DOUBLE_EQ(5 , Tokenizer::ParseFloat("5")); + EXPECT_DOUBLE_EQ(6e-12, Tokenizer::ParseFloat("6e-12")); + EXPECT_DOUBLE_EQ(1.2 , Tokenizer::ParseFloat("1.2")); + EXPECT_DOUBLE_EQ(1.e2 , Tokenizer::ParseFloat("1.e2")); + + // Test invalid integers that may still be tokenized as integers. + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1e")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1e-")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1.e")); + + // Test 'f' suffix. + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1f")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1.0f")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1F")); + + // These should parse successfully even though they are out of range. + // Overflows become infinity and underflows become zero. + EXPECT_EQ( 0.0, Tokenizer::ParseFloat("1e-9999999999999999999999999999")); + EXPECT_EQ(HUGE_VAL, Tokenizer::ParseFloat("1e+9999999999999999999999999999")); + +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + // Test invalid integers that will never be tokenized as integers. + EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("zxy"), + "passed text that could not have been tokenized as a float"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("1-e0"), + "passed text that could not have been tokenized as a float"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("-1.0"), + "passed text that could not have been tokenized as a float"); +#endif // GTEST_HAS_DEATH_TEST +} + +TEST_F(TokenizerTest, ParseString) { + string output; + Tokenizer::ParseString("'hello'", &output); + EXPECT_EQ("hello", output); + Tokenizer::ParseString("\"blah\\nblah2\"", &output); + EXPECT_EQ("blah\nblah2", output); + Tokenizer::ParseString("'\\1x\\1\\123\\739\\52\\334n\\3'", &output); + EXPECT_EQ("\1x\1\123\739\52\334n\3", output); + Tokenizer::ParseString("'\\x20\\x4'", &output); + EXPECT_EQ("\x20\x4", output); + + // Test invalid strings that may still be tokenized as strings. + Tokenizer::ParseString("\"\\a\\l\\v\\t", &output); // \l is invalid + EXPECT_EQ("\a?\v\t", output); + Tokenizer::ParseString("'", &output); + EXPECT_EQ("", output); + Tokenizer::ParseString("'\\", &output); + EXPECT_EQ("\\", output); + + // Test invalid strings that will never be tokenized as strings. +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + EXPECT_DEBUG_DEATH(Tokenizer::ParseString("", &output), + "passed text that could not have been tokenized as a string"); +#endif // GTEST_HAS_DEATH_TEST +} + +// ------------------------------------------------------------------- + +// Each case parses some input text, ignoring the tokens produced, and +// checks that the error output matches what is expected. +struct ErrorCase { + string input; + bool recoverable; // True if the tokenizer should be able to recover and + // parse more tokens after seeing this error. Cases + // for which this is true must end with "foo" as + // the last token, which the test will check for. + const char* errors; +}; + +inline ostream& operator<<(ostream& out, + const ErrorCase& test_case) { + return out << CEscape(test_case.input); +} + +ErrorCase kErrorCases[] = { + // String errors. + { "'\\l' foo", true, + "0:2: Invalid escape sequence in string literal.\n" }, + { "'\\x' foo", true, + "0:3: Expected hex digits for escape sequence.\n" }, + { "'foo", false, + "0:4: String literals cannot cross line boundaries.\n" }, + { "'bar\nfoo", true, + "0:4: String literals cannot cross line boundaries.\n" }, + + // Integer errors. + { "123foo", true, + "0:3: Need space between number and identifier.\n" }, + + // Hex/octal errors. + { "0x foo", true, + "0:2: \"0x\" must be followed by hex digits.\n" }, + { "0541823 foo", true, + "0:4: Numbers starting with leading zero must be in octal.\n" }, + { "0x123z foo", true, + "0:5: Need space between number and identifier.\n" }, + { "0x123.4 foo", true, + "0:5: Hex and octal numbers must be integers.\n" }, + { "0123.4 foo", true, + "0:4: Hex and octal numbers must be integers.\n" }, + + // Float errors. + { "1e foo", true, + "0:2: \"e\" must be followed by exponent.\n" }, + { "1e- foo", true, + "0:3: \"e\" must be followed by exponent.\n" }, + { "1.2.3 foo", true, + "0:3: Already saw decimal point or exponent; can't have another one.\n" }, + { "1e2.3 foo", true, + "0:3: Already saw decimal point or exponent; can't have another one.\n" }, + { "a.1 foo", true, + "0:1: Need space between identifier and decimal point.\n" }, + // allow_f_after_float not enabled, so this should be an error. + { "1.0f foo", true, + "0:3: Need space between number and identifier.\n" }, + + // Block comment errors. + { "/*", false, + "0:2: End-of-file inside block comment.\n" + "0:0: Comment started here.\n"}, + { "/*/*/ foo", true, + "0:3: \"/*\" inside block comment. Block comments cannot be nested.\n"}, + + // Control characters. Multiple consecutive control characters should only + // produce one error. + { "\b foo", true, + "0:0: Invalid control characters encountered in text.\n" }, + { "\b\b foo", true, + "0:0: Invalid control characters encountered in text.\n" }, + + // Check that control characters at end of input don't result in an + // infinite loop. + { "\b", false, + "0:0: Invalid control characters encountered in text.\n" }, + + // Check recovery from '\0'. We have to explicitly specify the length of + // these strings because otherwise the string constructor will just call + // strlen() which will see the first '\0' and think that is the end of the + // string. + { string("\0foo", 4), true, + "0:0: Invalid control characters encountered in text.\n" }, + { string("\0\0foo", 5), true, + "0:0: Invalid control characters encountered in text.\n" }, +}; + +TEST_2D(TokenizerTest, Errors, kErrorCases, kBlockSizes) { + // Set up the tokenizer. + TestInputStream input(kErrorCases_case.input.data(), + kErrorCases_case.input.size(), + kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + // Ignore all input, except remember if the last token was "foo". + bool last_was_foo = false; + while (tokenizer.Next()) { + last_was_foo = tokenizer.current().text == "foo"; + } + + // Check that the errors match what was expected. + EXPECT_EQ(error_collector.text_, kErrorCases_case.errors); + + // If the error was recoverable, make sure we saw "foo" after it. + if (kErrorCases_case.recoverable) { + EXPECT_TRUE(last_was_foo); + } +} + +// ------------------------------------------------------------------- + +TEST_1D(TokenizerTest, BackUpOnDestruction, kBlockSizes) { + string text = "foo bar"; + TestInputStream input(text.data(), text.size(), kBlockSizes_case); + + // Create a tokenizer, read one token, then destroy it. + { + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + tokenizer.Next(); + } + + // Only "foo" should have been read. + EXPECT_EQ(strlen("foo"), input.ByteCount()); +} + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/zero_copy_stream.cc b/src/google/protobuf/io/zero_copy_stream.cc new file mode 100644 index 00000000..80559c4a --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream.cc @@ -0,0 +1,34 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + + +namespace google { +namespace protobuf { +namespace io { + +ZeroCopyInputStream::~ZeroCopyInputStream() {} +ZeroCopyOutputStream::~ZeroCopyOutputStream() {} + + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/zero_copy_stream.h b/src/google/protobuf/io/zero_copy_stream.h new file mode 100644 index 00000000..bce5f2d3 --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream.h @@ -0,0 +1,224 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains the ZeroCopyInputStream and ZeroCopyOutputStream +// interfaces, which represent abstract I/O streams to and from which +// protocol buffers can be read and written. For a few simple +// implementations of these interfaces, see zero_copy_stream_impl.h. +// +// These interfaces are different from classic I/O streams in that they +// try to minimize the amount of data copying that needs to be done. +// To accomplish this, responsibility for allocating buffers is moved to +// the stream object, rather than being the responsibility of the caller. +// So, the stream can return a buffer which actually points directly into +// the final data structure where the bytes are to be stored, and the caller +// can interact directly with that buffer, eliminating an intermediate copy +// operation. +// +// As an example, consider the common case in which you are reading bytes +// from an array that is already in memory (or perhaps an mmap()ed file). +// With classic I/O streams, you would do something like: +// char buffer[BUFFER_SIZE]; +// input->Read(buffer, BUFFER_SIZE); +// DoSomething(buffer, BUFFER_SIZE); +// Then, the stream basically just calls memcpy() to copy the data from +// the array into your buffer. With a ZeroCopyInputStream, you would do +// this instead: +// const void* buffer; +// int size; +// input->Next(&buffer, &size); +// DoSomething(buffer, size); +// Here, no copy is performed. The input stream returns a pointer directly +// into the backing array, and the caller ends up reading directly from it. +// +// If you want to be able to read the old-fashion way, you can create +// a CodedInputStream or CodedOutputStream wrapping these objects and use +// their ReadRaw()/WriteRaw() methods. These will, of course, add a copy +// step, but Coded*Stream will handle buffering so at least it will be +// reasonably efficient. +// +// ZeroCopyInputStream example: +// // Read in a file and print its contents to stdout. +// int fd = open("myfile", O_RDONLY); +// ZeroCopyInputStream* input = new FileInputStream(fd); +// +// const void* buffer; +// int size; +// while (input->Next(&buffer, &size)) { +// cout.write(buffer, size); +// } +// +// delete input; +// close(fd); +// +// ZeroCopyOutputStream example: +// // Copy the contents of "infile" to "outfile", using plain read() for +// // "infile" but a ZeroCopyOutputStream for "outfile". +// int infd = open("infile", O_RDONLY); +// int outfd = open("outfile", O_WRONLY); +// ZeroCopyInputStream* output = new FileOutputStream(outfd); +// +// void* buffer; +// int size; +// while (output->Next(&buffer, &size)) { +// int bytes = read(infd, buffer, size); +// if (bytes < size) { +// // Reached EOF. +// output->BackUp(size - bytes); +// break; +// } +// } +// +// delete output; +// close(infd); +// close(outfd); + +#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__ +#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__ + +#include +#include + +namespace google { + +namespace protobuf { +namespace io { + +// Defined in this file. +class ZeroCopyInputStream; +class ZeroCopyOutputStream; + +// Abstract interface similar to an input stream but designed to minimize +// copying. +class LIBPROTOBUF_EXPORT ZeroCopyInputStream { + public: + inline ZeroCopyInputStream() {} + virtual ~ZeroCopyInputStream(); + + // Obtains a chunk of data from the stream. + // + // Preconditions: + // * "size" and "data" are not NULL. + // + // Postconditions: + // * If the returned value is false, there is no more data to return or + // an error occurred. All errors are permanent. + // * Otherwise, "size" points to the actual number of bytes read and "data" + // points to a pointer to a buffer containing these bytes. + // * Ownership of this buffer remains with the stream, and the buffer + // remains valid only until some other method of the stream is called + // or the stream is destroyed. + // * It is legal for the returned buffer to have zero size, as long + // as repeatedly calling Next() eventually yields a buffer with non-zero + // size. + virtual bool Next(const void** data, int* size) = 0; + + // Backs up a number of bytes, so that the next call to Next() returns + // data again that was already returned by the last call to Next(). This + // is useful when writing procedures that are only supposed to read up + // to a certain point in the input, then return. If Next() returns a + // buffer that goes beyond what you wanted to read, you can use BackUp() + // to return to the point where you intended to finish. + // + // Preconditions: + // * The last method called must have been Next(). + // * count must be less than or equal to the size of the last buffer + // returned by Next(). + // + // Postconditions: + // * The last "count" bytes of the last buffer returned by Next() will be + // pushed back into the stream. Subsequent calls to Next() will return + // the same data again before producing new data. + virtual void BackUp(int count) = 0; + + // Skips a number of bytes. Returns false if the end of the stream is + // reached or some input error occurred. In the end-of-stream case, the + // stream is advanced to the end of the stream (so ByteCount() will return + // the total size of the stream). + virtual bool Skip(int count) = 0; + + // Returns the total number of bytes read since this object was created. + virtual int64 ByteCount() const = 0; + + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyInputStream); +}; + +// Abstract interface similar to an output stream but designed to minimize +// copying. +class LIBPROTOBUF_EXPORT ZeroCopyOutputStream { + public: + inline ZeroCopyOutputStream() {} + virtual ~ZeroCopyOutputStream(); + + // Obtains a buffer into which data can be written. Any data written + // into this buffer will eventually (maybe instantly, maybe later on) + // be written to the output. + // + // Preconditions: + // * "size" and "data" are not NULL. + // + // Postconditions: + // * If the returned value is false, an error occurred. All errors are + // permanent. + // * Otherwise, "size" points to the actual number of bytes in the buffer + // and "data" points to the buffer. + // * Ownership of this buffer remains with the stream, and the buffer + // remains valid only until some other method of the stream is called + // or the stream is destroyed. + // * Any data which the caller stores in this buffer will eventually be + // written to the output (unless BackUp() is called). + // * It is legal for the returned buffer to have zero size, as long + // as repeatedly calling Next() eventually yields a buffer with non-zero + // size. + virtual bool Next(void** data, int* size) = 0; + + // Backs up a number of bytes, so that the end of the last buffer returned + // by Next() is not actually written. This is needed when you finish + // writing all the data you want to write, but the last buffer was bigger + // than you needed. You don't want to write a bunch of garbage after the + // end of your data, so you use BackUp() to back up. + // + // Preconditions: + // * The last method called must have been Next(). + // * count must be less than or equal to the size of the last buffer + // returned by Next(). + // * The caller must not have written anything to the last "count" bytes + // of that buffer. + // + // Postconditions: + // * The last "count" bytes of the last buffer returned by Next() will be + // ignored. + virtual void BackUp(int count) = 0; + + // Returns the total number of bytes written since this object was created. + virtual int64 ByteCount() const = 0; + + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyOutputStream); +}; + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__ diff --git a/src/google/protobuf/io/zero_copy_stream_impl.cc b/src/google/protobuf/io/zero_copy_stream_impl.cc new file mode 100644 index 00000000..7ff84460 --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream_impl.cc @@ -0,0 +1,793 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifdef _MSC_VER +#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +#ifdef _WIN32 +// Win32 lseek is broken: If invoked on a non-seekable file descriptor, its +// return value is undefined. We re-define it to always produce an error. +#define lseek(fd, offset, origin) ((off_t)-1) +#endif + +namespace { + + +// EINTR sucks. +int close_no_eintr(int fd) { + int result; + do { + result = close(fd); + } while (result < 0 && errno == EINTR); + return result; +} + +// Default block size for Copying{In,Out}putStreamAdaptor. +static const int kDefaultBlockSize = 8192; + +} // namespace + +// =================================================================== + +ArrayInputStream::ArrayInputStream(const void* data, int size, + int block_size) + : data_(reinterpret_cast(data)), + size_(size), + block_size_(block_size > 0 ? block_size : size), + position_(0), + last_returned_size_(0) { +} + +ArrayInputStream::~ArrayInputStream() { +} + +bool ArrayInputStream::Next(const void** data, int* size) { + if (position_ < size_) { + last_returned_size_ = min(block_size_, size_ - position_); + *data = data_ + position_; + *size = last_returned_size_; + position_ += last_returned_size_; + return true; + } else { + // We're at the end of the array. + last_returned_size_ = 0; // Don't let caller back up. + return false; + } +} + +void ArrayInputStream::BackUp(int count) { + GOOGLE_CHECK_GT(last_returned_size_, 0) + << "BackUp() can only be called after a successful Next()."; + GOOGLE_CHECK_LE(count, last_returned_size_); + GOOGLE_CHECK_GE(count, 0); + position_ -= count; + last_returned_size_ = 0; // Don't let caller back up further. +} + +bool ArrayInputStream::Skip(int count) { + GOOGLE_CHECK_GE(count, 0); + last_returned_size_ = 0; // Don't let caller back up. + if (count > size_ - position_) { + position_ = size_; + return false; + } else { + position_ += count; + return true; + } +} + +int64 ArrayInputStream::ByteCount() const { + return position_; +} + + +// =================================================================== + +ArrayOutputStream::ArrayOutputStream(void* data, int size, int block_size) + : data_(reinterpret_cast(data)), + size_(size), + block_size_(block_size > 0 ? block_size : size), + position_(0), + last_returned_size_(0) { +} + +ArrayOutputStream::~ArrayOutputStream() { +} + +bool ArrayOutputStream::Next(void** data, int* size) { + if (position_ < size_) { + last_returned_size_ = min(block_size_, size_ - position_); + *data = data_ + position_; + *size = last_returned_size_; + position_ += last_returned_size_; + return true; + } else { + // We're at the end of the array. + last_returned_size_ = 0; // Don't let caller back up. + return false; + } +} + +void ArrayOutputStream::BackUp(int count) { + GOOGLE_CHECK_GT(last_returned_size_, 0) + << "BackUp() can only be called after a successful Next()."; + GOOGLE_CHECK_LE(count, last_returned_size_); + GOOGLE_CHECK_GE(count, 0); + position_ -= count; + last_returned_size_ = 0; // Don't let caller back up further. +} + +int64 ArrayOutputStream::ByteCount() const { + return position_; +} + +// =================================================================== + +StringOutputStream::StringOutputStream(string* target) + : target_(target) { +} + +StringOutputStream::~StringOutputStream() { +} + +bool StringOutputStream::Next(void** data, int* size) { + int old_size = target_->size(); + + // Grow the string. + if (old_size < target_->capacity()) { + // Resize the string to match its capacity, since we can get away + // without a memory allocation this way. + target_->resize(target_->capacity()); + } else { + // Size has reached capacity, so double the size. Also make sure + // that the new size is at least kMinimumSize. + target_->resize( + max(old_size * 2, + kMinimumSize + 0)); // "+ 0" works around GCC4 weirdness. + } + + *data = string_as_array(target_) + old_size; + *size = target_->size() - old_size; + return true; +} + +void StringOutputStream::BackUp(int count) { + GOOGLE_CHECK_GE(count, 0); + GOOGLE_CHECK_LE(count, target_->size()); + target_->resize(target_->size() - count); +} + +int64 StringOutputStream::ByteCount() const { + return target_->size(); +} + +// =================================================================== + + +// =================================================================== + +CopyingInputStream::~CopyingInputStream() {} + +int CopyingInputStream::Skip(int count) { + char junk[4096]; + int skipped = 0; + while (skipped < count) { + int bytes = Read(junk, min(count, implicit_cast(sizeof(junk)))); + if (bytes <= 0) { + // EOF or read error. + return skipped; + } + skipped += bytes; + } + return skipped; +} + +CopyingInputStreamAdaptor::CopyingInputStreamAdaptor( + CopyingInputStream* copying_stream, int block_size) + : copying_stream_(copying_stream), + owns_copying_stream_(false), + failed_(false), + position_(0), + buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize), + buffer_used_(0), + backup_bytes_(0) { +} + +CopyingInputStreamAdaptor::~CopyingInputStreamAdaptor() { + if (owns_copying_stream_) { + delete copying_stream_; + } +} + +bool CopyingInputStreamAdaptor::Next(const void** data, int* size) { + if (failed_) { + // Already failed on a previous read. + return false; + } + + AllocateBufferIfNeeded(); + + if (backup_bytes_ > 0) { + // We have data left over from a previous BackUp(), so just return that. + *data = buffer_.get() + buffer_used_ - backup_bytes_; + *size = backup_bytes_; + backup_bytes_ = 0; + return true; + } + + // Read new data into the buffer. + buffer_used_ = copying_stream_->Read(buffer_.get(), buffer_size_); + if (buffer_used_ <= 0) { + // EOF or read error. We don't need the buffer anymore. + if (buffer_used_ < 0) { + // Read error (not EOF). + failed_ = true; + } + FreeBuffer(); + return false; + } + position_ += buffer_used_; + + *size = buffer_used_; + *data = buffer_.get(); + return true; +} + +void CopyingInputStreamAdaptor::BackUp(int count) { + GOOGLE_CHECK(backup_bytes_ == 0 && buffer_.get() != NULL) + << " BackUp() can only be called after Next()."; + GOOGLE_CHECK_LE(count, buffer_used_) + << " Can't back up over more bytes than were returned by the last call" + " to Next()."; + GOOGLE_CHECK_GE(count, 0) + << " Parameter to BackUp() can't be negative."; + + backup_bytes_ = count; +} + +bool CopyingInputStreamAdaptor::Skip(int count) { + GOOGLE_CHECK_GE(count, 0); + + if (failed_) { + // Already failed on a previous read. + return false; + } + + // First skip any bytes left over from a previous BackUp(). + if (backup_bytes_ >= count) { + // We have more data left over than we're trying to skip. Just chop it. + backup_bytes_ -= count; + return true; + } + + count -= backup_bytes_; + backup_bytes_ = 0; + + int skipped = copying_stream_->Skip(count); + position_ += skipped; + return skipped == count; +} + +int64 CopyingInputStreamAdaptor::ByteCount() const { + return position_ - backup_bytes_; +} + +void CopyingInputStreamAdaptor::AllocateBufferIfNeeded() { + if (buffer_.get() == NULL) { + buffer_.reset(new uint8[buffer_size_]); + } +} + +void CopyingInputStreamAdaptor::FreeBuffer() { + GOOGLE_CHECK_EQ(backup_bytes_, 0); + buffer_used_ = 0; + buffer_.reset(); +} + +// =================================================================== + +CopyingOutputStream::~CopyingOutputStream() {} + +CopyingOutputStreamAdaptor::CopyingOutputStreamAdaptor( + CopyingOutputStream* copying_stream, int block_size) + : copying_stream_(copying_stream), + owns_copying_stream_(false), + failed_(false), + position_(0), + buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize), + buffer_used_(0) { +} + +CopyingOutputStreamAdaptor::~CopyingOutputStreamAdaptor() { + WriteBuffer(); + if (owns_copying_stream_) { + delete copying_stream_; + } +} + +bool CopyingOutputStreamAdaptor::Flush() { + return WriteBuffer(); +} + +bool CopyingOutputStreamAdaptor::Next(void** data, int* size) { + if (buffer_used_ == buffer_size_) { + if (!WriteBuffer()) return false; + } + + AllocateBufferIfNeeded(); + + *data = buffer_.get() + buffer_used_; + *size = buffer_size_ - buffer_used_; + buffer_used_ = buffer_size_; + return true; +} + +void CopyingOutputStreamAdaptor::BackUp(int count) { + GOOGLE_CHECK_GE(count, 0); + GOOGLE_CHECK_EQ(buffer_used_, buffer_size_) + << " BackUp() can only be called after Next()."; + GOOGLE_CHECK_LE(count, buffer_used_) + << " Can't back up over more bytes than were returned by the last call" + " to Next()."; + + buffer_used_ -= count; +} + +int64 CopyingOutputStreamAdaptor::ByteCount() const { + return position_ + buffer_used_; +} + +bool CopyingOutputStreamAdaptor::WriteBuffer() { + if (failed_) { + // Already failed on a previous write. + return false; + } + + if (buffer_used_ == 0) return true; + + if (copying_stream_->Write(buffer_.get(), buffer_used_)) { + position_ += buffer_used_; + buffer_used_ = 0; + return true; + } else { + failed_ = true; + FreeBuffer(); + return false; + } +} + +void CopyingOutputStreamAdaptor::AllocateBufferIfNeeded() { + if (buffer_ == NULL) { + buffer_.reset(new uint8[buffer_size_]); + } +} + +void CopyingOutputStreamAdaptor::FreeBuffer() { + buffer_used_ = 0; + buffer_.reset(); +} + +// =================================================================== + +FileInputStream::FileInputStream(int file_descriptor, int block_size) + : copying_input_(file_descriptor), + impl_(©ing_input_, block_size) { +} + +FileInputStream::~FileInputStream() {} + +bool FileInputStream::Close() { + return copying_input_.Close(); +} + +bool FileInputStream::Next(const void** data, int* size) { + return impl_.Next(data, size); +} + +void FileInputStream::BackUp(int count) { + impl_.BackUp(count); +} + +bool FileInputStream::Skip(int count) { + return impl_.Skip(count); +} + +int64 FileInputStream::ByteCount() const { + return impl_.ByteCount(); +} + +FileInputStream::CopyingFileInputStream::CopyingFileInputStream( + int file_descriptor) + : file_(file_descriptor), + close_on_delete_(false), + is_closed_(false), + errno_(0), + previous_seek_failed_(false) { +} + +FileInputStream::CopyingFileInputStream::~CopyingFileInputStream() { + if (close_on_delete_) { + if (!Close()) { + GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_); + } + } +} + +bool FileInputStream::CopyingFileInputStream::Close() { + GOOGLE_CHECK(!is_closed_); + + is_closed_ = true; + if (close_no_eintr(file_) != 0) { + // The docs on close() do not specify whether a file descriptor is still + // open after close() fails with EIO. However, the glibc source code + // seems to indicate that it is not. + errno_ = errno; + return false; + } + + return true; +} + +int FileInputStream::CopyingFileInputStream::Read(void* buffer, int size) { + GOOGLE_CHECK(!is_closed_); + + int result; + do { + result = read(file_, buffer, size); + } while (result < 0 && errno == EINTR); + + if (result < 0) { + // Read error (not EOF). + errno_ = errno; + } + + return result; +} + +int FileInputStream::CopyingFileInputStream::Skip(int count) { + GOOGLE_CHECK(!is_closed_); + + if (!previous_seek_failed_ && + lseek(file_, count, SEEK_CUR) != (off_t)-1) { + // Seek succeeded. + return count; + } else { + // Failed to seek. + + // Note to self: Don't seek again. This file descriptor doesn't + // support it. + previous_seek_failed_ = true; + + // Use the default implementation. + return CopyingInputStream::Skip(count); + } +} + +// =================================================================== + +FileOutputStream::FileOutputStream(int file_descriptor, int block_size) + : copying_output_(file_descriptor), + impl_(©ing_output_, block_size) { +} + +FileOutputStream::~FileOutputStream() { + impl_.Flush(); +} + +bool FileOutputStream::Close() { + bool flush_succeeded = impl_.Flush(); + return copying_output_.Close() && flush_succeeded; +} + +bool FileOutputStream::Next(void** data, int* size) { + return impl_.Next(data, size); +} + +void FileOutputStream::BackUp(int count) { + impl_.BackUp(count); +} + +int64 FileOutputStream::ByteCount() const { + return impl_.ByteCount(); +} + +FileOutputStream::CopyingFileOutputStream::CopyingFileOutputStream( + int file_descriptor) + : file_(file_descriptor), + close_on_delete_(false), + is_closed_(false), + errno_(0) { +} + +FileOutputStream::CopyingFileOutputStream::~CopyingFileOutputStream() { + if (close_on_delete_) { + if (!Close()) { + GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_); + } + } +} + +bool FileOutputStream::CopyingFileOutputStream::Close() { + GOOGLE_CHECK(!is_closed_); + + is_closed_ = true; + if (close_no_eintr(file_) != 0) { + // The docs on close() do not specify whether a file descriptor is still + // open after close() fails with EIO. However, the glibc source code + // seems to indicate that it is not. + errno_ = errno; + return false; + } + + return true; +} + +bool FileOutputStream::CopyingFileOutputStream::Write( + const void* buffer, int size) { + GOOGLE_CHECK(!is_closed_); + int total_written = 0; + + const uint8* buffer_base = reinterpret_cast(buffer); + + while (total_written < size) { + int bytes; + do { + bytes = write(file_, buffer_base + total_written, size - total_written); + } while (bytes < 0 && errno == EINTR); + + if (bytes <= 0) { + // Write error. + + // FIXME(kenton): According to the man page, if write() returns zero, + // there was no error; write() simply did not write anything. It's + // unclear under what circumstances this might happen, but presumably + // errno won't be set in this case. I am confused as to how such an + // event should be handled. For now I'm treating it as an error, since + // retrying seems like it could lead to an infinite loop. I suspect + // this never actually happens anyway. + + if (bytes < 0) { + errno_ = errno; + } + return false; + } + total_written += bytes; + } + + return true; +} + +// =================================================================== + +IstreamInputStream::IstreamInputStream(istream* input, int block_size) + : copying_input_(input), + impl_(©ing_input_, block_size) { +} + +IstreamInputStream::~IstreamInputStream() {} + +bool IstreamInputStream::Next(const void** data, int* size) { + return impl_.Next(data, size); +} + +void IstreamInputStream::BackUp(int count) { + impl_.BackUp(count); +} + +bool IstreamInputStream::Skip(int count) { + return impl_.Skip(count); +} + +int64 IstreamInputStream::ByteCount() const { + return impl_.ByteCount(); +} + +IstreamInputStream::CopyingIstreamInputStream::CopyingIstreamInputStream( + istream* input) + : input_(input) { +} + +IstreamInputStream::CopyingIstreamInputStream::~CopyingIstreamInputStream() {} + +int IstreamInputStream::CopyingIstreamInputStream::Read( + void* buffer, int size) { + input_->read(reinterpret_cast(buffer), size); + int result = input_->gcount(); + if (result == 0 && input_->fail() && !input_->eof()) { + return -1; + } + return result; +} + +// =================================================================== + +OstreamOutputStream::OstreamOutputStream(ostream* output, int block_size) + : copying_output_(output), + impl_(©ing_output_, block_size) { +} + +OstreamOutputStream::~OstreamOutputStream() { + impl_.Flush(); +} + +bool OstreamOutputStream::Next(void** data, int* size) { + return impl_.Next(data, size); +} + +void OstreamOutputStream::BackUp(int count) { + impl_.BackUp(count); +} + +int64 OstreamOutputStream::ByteCount() const { + return impl_.ByteCount(); +} + +OstreamOutputStream::CopyingOstreamOutputStream::CopyingOstreamOutputStream( + ostream* output) + : output_(output) { +} + +OstreamOutputStream::CopyingOstreamOutputStream::~CopyingOstreamOutputStream() { +} + +bool OstreamOutputStream::CopyingOstreamOutputStream::Write( + const void* buffer, int size) { + output_->write(reinterpret_cast(buffer), size); + return output_->good(); +} + +// =================================================================== + +ConcatenatingInputStream::ConcatenatingInputStream( + ZeroCopyInputStream* const streams[], int count) + : streams_(streams), stream_count_(count), bytes_retired_(0) { +} + +ConcatenatingInputStream::~ConcatenatingInputStream() { +} + +bool ConcatenatingInputStream::Next(const void** data, int* size) { + while (stream_count_ > 0) { + if (streams_[0]->Next(data, size)) return true; + + // That stream is done. Advance to the next one. + bytes_retired_ += streams_[0]->ByteCount(); + ++streams_; + --stream_count_; + } + + // No more streams. + return false; +} + +void ConcatenatingInputStream::BackUp(int count) { + if (stream_count_ > 0) { + streams_[0]->BackUp(count); + } else { + GOOGLE_LOG(DFATAL) << "Can't BackUp() after failed Next()."; + } +} + +bool ConcatenatingInputStream::Skip(int count) { + while (stream_count_ > 0) { + // Assume that ByteCount() can be used to find out how much we actually + // skipped when Skip() fails. + int64 target_byte_count = streams_[0]->ByteCount() + count; + if (streams_[0]->Skip(count)) return true; + + // Hit the end of the stream. Figure out how many more bytes we still have + // to skip. + int64 final_byte_count = streams_[0]->ByteCount(); + GOOGLE_DCHECK_LT(final_byte_count, target_byte_count); + count = target_byte_count - final_byte_count; + + // That stream is done. Advance to the next one. + bytes_retired_ += final_byte_count; + ++streams_; + --stream_count_; + } + + return false; +} + +int64 ConcatenatingInputStream::ByteCount() const { + if (stream_count_ == 0) { + return bytes_retired_; + } else { + return bytes_retired_ + streams_[0]->ByteCount(); + } +} + + +// =================================================================== + +LimitingInputStream::LimitingInputStream(ZeroCopyInputStream* input, + int64 limit) + : input_(input), limit_(limit) {} + +LimitingInputStream::~LimitingInputStream() { + // If we overshot the limit, back up. + if (limit_ < 0) input_->BackUp(-limit_); +} + +bool LimitingInputStream::Next(const void** data, int* size) { + if (limit_ < 0) return false; + if (!input_->Next(data, size)) return false; + + limit_ -= *size; + if (limit_ < 0) { + // We overshot the limit. Reduce *size to hide the rest of the buffer. + *size += limit_; + } + return true; +} + +void LimitingInputStream::BackUp(int count) { + if (limit_ < 0) { + input_->BackUp(count - limit_); + limit_ = count; + } else { + input_->BackUp(count); + limit_ += count; + } +} + +bool LimitingInputStream::Skip(int count) { + if (count > limit_) { + if (limit_ < 0) return false; + input_->Skip(limit_); + limit_ = 0; + return false; + } else { + if (!input_->Skip(count)) return false; + limit_ -= count; + return true; + } +} + +int64 LimitingInputStream::ByteCount() const { + if (limit_ < 0) { + return input_->ByteCount() + limit_; + } else { + return input_->ByteCount(); + } +} + + +// =================================================================== + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/zero_copy_stream_impl.h b/src/google/protobuf/io/zero_copy_stream_impl.h new file mode 100644 index 00000000..bd73afb7 --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream_impl.h @@ -0,0 +1,617 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains common implementations of the interfaces defined in +// zero_copy_stream.h. These implementations cover I/O on raw arrays, +// strings, and file descriptors. Of course, many users will probably +// want to write their own implementations of these interfaces specific +// to the particular I/O abstractions they prefer to use, but these +// should cover the most common cases. + +#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__ +#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__ + +#include +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace io { + +// =================================================================== + +// A ZeroCopyInputStream backed by an in-memory array of bytes. +class LIBPROTOBUF_EXPORT ArrayInputStream : public ZeroCopyInputStream { + public: + // Create an InputStream that returns the bytes pointed to by "data". + // "data" remains the property of the caller but must remain valid until + // the stream is destroyed. If a block_size is given, calls to Next() + // will return data blocks no larger than the given size. Otherwise, the + // first call to Next() returns the entire array. block_size is mainly + // useful for testing; in production you would probably never want to set + // it. + ArrayInputStream(const void* data, int size, int block_size = -1); + ~ArrayInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + + private: + const uint8* const data_; // The byte array. + const int size_; // Total size of the array. + const int block_size_; // How many bytes to return at a time. + + int position_; + int last_returned_size_; // How many bytes we returned last time Next() + // was called (used for error checking only). + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayInputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream backed by an in-memory array of bytes. +class LIBPROTOBUF_EXPORT ArrayOutputStream : public ZeroCopyOutputStream { + public: + // Create an OutputStream that writes to the bytes pointed to by "data". + // "data" remains the property of the caller but must remain valid until + // the stream is destroyed. If a block_size is given, calls to Next() + // will return data blocks no larger than the given size. Otherwise, the + // first call to Next() returns the entire array. block_size is mainly + // useful for testing; in production you would probably never want to set + // it. + ArrayOutputStream(void* data, int size, int block_size = -1); + ~ArrayOutputStream(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + uint8* const data_; // The byte array. + const int size_; // Total size of the array. + const int block_size_; // How many bytes to return at a time. + + int position_; + int last_returned_size_; // How many bytes we returned last time Next() + // was called (used for error checking only). + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayOutputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream which appends bytes to a string. +class LIBPROTOBUF_EXPORT StringOutputStream : public ZeroCopyOutputStream { + public: + // Create a StringOutputStream which appends bytes to the given string. + // The string remains property of the caller, but it MUST NOT be accessed + // in any way until the stream is destroyed. + // + // Hint: If you call target->reserve(n) before creating the stream, + // the first call to Next() will return at least n bytes of buffer + // space. + explicit StringOutputStream(string* target); + ~StringOutputStream(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + static const int kMinimumSize = 16; + + string* target_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOutputStream); +}; + +// Note: There is no StringInputStream. Instead, just create an +// ArrayInputStream as follows: +// ArrayInputStream input(str.data(), str.size()); + +// =================================================================== + + +// =================================================================== + +// A generic traditional input stream interface. +// +// Lots of traditional input streams (e.g. file descriptors, C stdio +// streams, and C++ iostreams) expose an interface where every read +// involves copying bytes into a buffer. If you want to take such an +// interface and make a ZeroCopyInputStream based on it, simply implement +// CopyingInputStream and then use CopyingInputStreamAdaptor. +// +// CopyingInputStream implementations should avoid buffering if possible. +// CopyingInputStreamAdaptor does its own buffering and will read data +// in large blocks. +class LIBPROTOBUF_EXPORT CopyingInputStream { + public: + virtual ~CopyingInputStream(); + + // Reads up to "size" bytes into the given buffer. Returns the number of + // bytes read. Read() waits until at least one byte is available, or + // returns zero if no bytes will ever become available (EOF), or -1 if a + // permanent read error occurred. + virtual int Read(void* buffer, int size) = 0; + + // Skips the next "count" bytes of input. Returns the number of bytes + // actually skipped. This will always be exactly equal to "count" unless + // EOF was reached or a permanent read error occurred. + // + // The default implementation just repeatedly calls Read() into a scratch + // buffer. + virtual int Skip(int count); +}; + +// A ZeroCopyInputStream which reads from a CopyingInputStream. This is +// useful for implementing ZeroCopyInputStreams that read from traditional +// streams. Note that this class is not really zero-copy. +// +// If you want to read from file descriptors or C++ istreams, this is +// already implemented for you: use FileInputStream or IstreamInputStream +// respectively. +class LIBPROTOBUF_EXPORT CopyingInputStreamAdaptor : public ZeroCopyInputStream { + public: + // Creates a stream that reads from the given CopyingInputStream. + // If a block_size is given, it specifies the number of bytes that + // should be read and returned with each call to Next(). Otherwise, + // a reasonable default is used. The caller retains ownership of + // copying_stream unless SetOwnsCopyingStream(true) is called. + explicit CopyingInputStreamAdaptor(CopyingInputStream* copying_stream, + int block_size = -1); + ~CopyingInputStreamAdaptor(); + + // Call SetOwnsCopyingStream(true) to tell the CopyingInputStreamAdaptor to + // delete the underlying CopyingInputStream when it is destroyed. + void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; } + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + private: + // Insures that buffer_ is not NULL. + void AllocateBufferIfNeeded(); + // Frees the buffer and resets buffer_used_. + void FreeBuffer(); + + // The underlying copying stream. + CopyingInputStream* copying_stream_; + bool owns_copying_stream_; + + // True if we have seen a permenant error from the underlying stream. + bool failed_; + + // The current position of copying_stream_, relative to the point where + // we started reading. + int64 position_; + + // Data is read into this buffer. It may be NULL if no buffer is currently + // in use. Otherwise, it points to an array of size buffer_size_. + scoped_array buffer_; + const int buffer_size_; + + // Number of valid bytes currently in the buffer (i.e. the size last + // returned by Next()). 0 <= buffer_used_ <= buffer_size_. + int buffer_used_; + + // Number of bytes in the buffer which were backed up over by a call to + // BackUp(). These need to be returned again. + // 0 <= backup_bytes_ <= buffer_used_ + int backup_bytes_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingInputStreamAdaptor); +}; + +// =================================================================== + +// A generic traditional output stream interface. +// +// Lots of traditional output streams (e.g. file descriptors, C stdio +// streams, and C++ iostreams) expose an interface where every write +// involves copying bytes from a buffer. If you want to take such an +// interface and make a ZeroCopyOutputStream based on it, simply implement +// CopyingOutputStream and then use CopyingOutputStreamAdaptor. +// +// CopyingOutputStream implementations should avoid buffering if possible. +// CopyingOutputStreamAdaptor does its own buffering and will write data +// in large blocks. +class LIBPROTOBUF_EXPORT CopyingOutputStream { + public: + virtual ~CopyingOutputStream(); + + // Writes "size" bytes from the given buffer to the output. Returns true + // if successful, false on a write error. + virtual bool Write(const void* buffer, int size) = 0; +}; + +// A ZeroCopyOutputStream which writes to a CopyingOutputStream. This is +// useful for implementing ZeroCopyOutputStreams that write to traditional +// streams. Note that this class is not really zero-copy. +// +// If you want to write to file descriptors or C++ ostreams, this is +// already implemented for you: use FileOutputStream or OstreamOutputStream +// respectively. +class LIBPROTOBUF_EXPORT CopyingOutputStreamAdaptor : public ZeroCopyOutputStream { + public: + // Creates a stream that writes to the given Unix file descriptor. + // If a block_size is given, it specifies the size of the buffers + // that should be returned by Next(). Otherwise, a reasonable default + // is used. + explicit CopyingOutputStreamAdaptor(CopyingOutputStream* copying_stream, + int block_size = -1); + ~CopyingOutputStreamAdaptor(); + + // Writes all pending data to the underlying stream. Returns false if a + // write error occurred on the underlying stream. (The underlying + // stream itself is not necessarily flushed.) + bool Flush(); + + // Call SetOwnsCopyingStream(true) to tell the CopyingOutputStreamAdaptor to + // delete the underlying CopyingOutputStream when it is destroyed. + void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; } + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + // Write the current buffer, if it is present. + bool WriteBuffer(); + // Insures that buffer_ is not NULL. + void AllocateBufferIfNeeded(); + // Frees the buffer. + void FreeBuffer(); + + // The underlying copying stream. + CopyingOutputStream* copying_stream_; + bool owns_copying_stream_; + + // True if we have seen a permenant error from the underlying stream. + bool failed_; + + // The current position of copying_stream_, relative to the point where + // we started writing. + int64 position_; + + // Data is written from this buffer. It may be NULL if no buffer is + // currently in use. Otherwise, it points to an array of size buffer_size_. + scoped_array buffer_; + const int buffer_size_; + + // Number of valid bytes currently in the buffer (i.e. the size last + // returned by Next()). When BackUp() is called, we just reduce this. + // 0 <= buffer_used_ <= buffer_size_. + int buffer_used_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOutputStreamAdaptor); +}; + +// =================================================================== + +// A ZeroCopyInputStream which reads from a file descriptor. +// +// FileInputStream is preferred over using an ifstream with IstreamInputStream. +// The latter will introduce an extra layer of buffering, harming performance. +// Also, it's conceivable that FileInputStream could someday be enhanced +// to use zero-copy file descriptors on OSs which support them. +class LIBPROTOBUF_EXPORT FileInputStream : public ZeroCopyInputStream { + public: + // Creates a stream that reads from the given Unix file descriptor. + // If a block_size is given, it specifies the number of bytes that + // should be read and returned with each call to Next(). Otherwise, + // a reasonable default is used. + explicit FileInputStream(int file_descriptor, int block_size = -1); + ~FileInputStream(); + + // Flushes any buffers and closes the underlying file. Returns false if + // an error occurs during the process; use GetErrno() to examine the error. + // Even if an error occurs, the file descriptor is closed when this returns. + bool Close(); + + // By default, the file descriptor is not closed when the stream is + // destroyed. Call SetCloseOnDelete(true) to change that. WARNING: + // This leaves no way for the caller to detect if close() fails. If + // detecting close() errors is important to you, you should arrange + // to close the descriptor yourself. + void SetCloseOnDelete(bool value) { copying_input_.SetCloseOnDelete(value); } + + // If an I/O error has occurred on this file descriptor, this is the + // errno from that error. Otherwise, this is zero. Once an error + // occurs, the stream is broken and all subsequent operations will + // fail. + int GetErrno() { return copying_input_.GetErrno(); } + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingFileInputStream : public CopyingInputStream { + public: + CopyingFileInputStream(int file_descriptor); + ~CopyingFileInputStream(); + + bool Close(); + void SetCloseOnDelete(bool value) { close_on_delete_ = value; } + int GetErrno() { return errno_; } + + // implements CopyingInputStream --------------------------------- + int Read(void* buffer, int size); + int Skip(int count); + + private: + // The file descriptor. + const int file_; + bool close_on_delete_; + bool is_closed_; + + // The errno of the I/O error, if one has occurred. Otherwise, zero. + int errno_; + + // Did we try to seek once and fail? If so, we assume this file descriptor + // doesn't support seeking and won't try again. + bool previous_seek_failed_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileInputStream); + }; + + CopyingFileInputStream copying_input_; + CopyingInputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileInputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream which writes to a file descriptor. +// +// FileInputStream is preferred over using an ofstream with OstreamOutputStream. +// The latter will introduce an extra layer of buffering, harming performance. +// Also, it's conceivable that FileInputStream could someday be enhanced +// to use zero-copy file descriptors on OSs which support them. +class LIBPROTOBUF_EXPORT FileOutputStream : public ZeroCopyOutputStream { + public: + // Creates a stream that writes to the given Unix file descriptor. + // If a block_size is given, it specifies the size of the buffers + // that should be returned by Next(). Otherwise, a reasonable default + // is used. + explicit FileOutputStream(int file_descriptor, int block_size = -1); + ~FileOutputStream(); + + // Flushes any buffers and closes the underlying file. Returns false if + // an error occurs during the process; use GetErrno() to examine the error. + // Even if an error occurs, the file descriptor is closed when this returns. + bool Close(); + + // By default, the file descriptor is not closed when the stream is + // destroyed. Call SetCloseOnDelete(true) to change that. WARNING: + // This leaves no way for the caller to detect if close() fails. If + // detecting close() errors is important to you, you should arrange + // to close the descriptor yourself. + void SetCloseOnDelete(bool value) { copying_output_.SetCloseOnDelete(value); } + + // If an I/O error has occurred on this file descriptor, this is the + // errno from that error. Otherwise, this is zero. Once an error + // occurs, the stream is broken and all subsequent operations will + // fail. + int GetErrno() { return copying_output_.GetErrno(); } + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingFileOutputStream : public CopyingOutputStream { + public: + CopyingFileOutputStream(int file_descriptor); + ~CopyingFileOutputStream(); + + bool Close(); + void SetCloseOnDelete(bool value) { close_on_delete_ = value; } + int GetErrno() { return errno_; } + + // implements CopyingOutputStream -------------------------------- + bool Write(const void* buffer, int size); + + private: + // The file descriptor. + const int file_; + bool close_on_delete_; + bool is_closed_; + + // The errno of the I/O error, if one has occurred. Otherwise, zero. + int errno_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileOutputStream); + }; + + CopyingFileOutputStream copying_output_; + CopyingOutputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileOutputStream); +}; + +// =================================================================== + +// A ZeroCopyInputStream which reads from a C++ istream. +// +// Note that for reading files (or anything represented by a file descriptor), +// FileInputStream is more efficient. +class LIBPROTOBUF_EXPORT IstreamInputStream : public ZeroCopyInputStream { + public: + // Creates a stream that reads from the given C++ istream. + // If a block_size is given, it specifies the number of bytes that + // should be read and returned with each call to Next(). Otherwise, + // a reasonable default is used. + explicit IstreamInputStream(istream* stream, int block_size = -1); + ~IstreamInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingIstreamInputStream : public CopyingInputStream { + public: + CopyingIstreamInputStream(istream* input); + ~CopyingIstreamInputStream(); + + // implements CopyingInputStream --------------------------------- + int Read(void* buffer, int size); + // (We use the default implementation of Skip().) + + private: + // The stream. + istream* input_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingIstreamInputStream); + }; + + CopyingIstreamInputStream copying_input_; + CopyingInputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(IstreamInputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream which writes to a C++ ostream. +// +// Note that for writing files (or anything represented by a file descriptor), +// FileOutputStream is more efficient. +class LIBPROTOBUF_EXPORT OstreamOutputStream : public ZeroCopyOutputStream { + public: + // Creates a stream that writes to the given C++ ostream. + // If a block_size is given, it specifies the size of the buffers + // that should be returned by Next(). Otherwise, a reasonable default + // is used. + explicit OstreamOutputStream(ostream* stream, int block_size = -1); + ~OstreamOutputStream(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingOstreamOutputStream : public CopyingOutputStream { + public: + CopyingOstreamOutputStream(ostream* output); + ~CopyingOstreamOutputStream(); + + // implements CopyingOutputStream -------------------------------- + bool Write(const void* buffer, int size); + + private: + // The stream. + ostream* output_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOstreamOutputStream); + }; + + CopyingOstreamOutputStream copying_output_; + CopyingOutputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OstreamOutputStream); +}; + +// =================================================================== + +// A ZeroCopyInputStream which reads from several other streams in sequence. +// ConcatenatingInputStream is unable to distinguish between end-of-stream +// and read errors in the underlying streams, so it assumes any errors mean +// end-of-stream. So, if the underlying streams fail for any other reason, +// ConcatenatingInputStream may do odd things. It is suggested that you do +// not use ConcatenatingInputStream on streams that might produce read errors +// other than end-of-stream. +class LIBPROTOBUF_EXPORT ConcatenatingInputStream : public ZeroCopyInputStream { + public: + // All streams passed in as well as the array itself must remain valid + // until the ConcatenatingInputStream is destroyed. + ConcatenatingInputStream(ZeroCopyInputStream* const streams[], int count); + ~ConcatenatingInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + + private: + // As streams are retired, streams_ is incremented and count_ is + // decremented. + ZeroCopyInputStream* const* streams_; + int stream_count_; + int64 bytes_retired_; // Bytes read from previous streams. + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ConcatenatingInputStream); +}; + +// =================================================================== + +// A ZeroCopyInputStream which wraps some other stream and limits it to +// a particular byte count. +class LIBPROTOBUF_EXPORT LimitingInputStream : public ZeroCopyInputStream { + public: + LimitingInputStream(ZeroCopyInputStream* input, int64 limit); + ~LimitingInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + + private: + ZeroCopyInputStream* input_; + int64 limit_; // Decreases as we go, becomes negative if we overshoot. + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LimitingInputStream); +}; + +// =================================================================== + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__ diff --git a/src/google/protobuf/io/zero_copy_stream_unittest.cc b/src/google/protobuf/io/zero_copy_stream_unittest.cc new file mode 100644 index 00000000..c618041f --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream_unittest.cc @@ -0,0 +1,443 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Testing strategy: For each type of I/O (array, string, file, etc.) we +// create an output stream and write some data to it, then create a +// corresponding input stream to read the same data back and expect it to +// match. When the data is written, it is written in several small chunks +// of varying sizes, with a BackUp() after each chunk. It is read back +// similarly, but with chunks separated at different points. The whole +// process is run with a variety of block sizes for both the input and +// the output. +// +// TODO(kenton): Rewrite this test to bring it up to the standards of all +// the other proto2 tests. May want to wait for gTest to implement +// "parametized tests" so that one set of tests can be used on all the +// implementations. + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +#ifdef _WIN32 +#define pipe(fds) _pipe(fds, 4096, O_BINARY) +#endif + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +class IoTest : public testing::Test { + protected: + // Test helpers. + + // Helper to write an array of data to an output stream. + bool WriteToOutput(ZeroCopyOutputStream* output, const void* data, int size); + // Helper to read a fixed-length array of data from an input stream. + int ReadFromInput(ZeroCopyInputStream* input, void* data, int size); + // Write a string to the output stream. + void WriteString(ZeroCopyOutputStream* output, const char* str); + // Read a number of bytes equal to the size of the given string and checks + // that it matches the string. + void ReadString(ZeroCopyInputStream* input, const char* str); + // Writes some text to the output stream in a particular order. Returns + // the number of bytes written, incase the caller needs that to set up an + // input stream. + int WriteStuff(ZeroCopyOutputStream* output); + // Reads text from an input stream and expects it to match what + // WriteStuff() writes. + void ReadStuff(ZeroCopyInputStream* input); + + static const int kBlockSizes[]; + static const int kBlockSizeCount; +}; + +const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64}; +const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes); + +bool IoTest::WriteToOutput(ZeroCopyOutputStream* output, + const void* data, int size) { + const uint8* in = reinterpret_cast(data); + int in_size = size; + + void* out; + int out_size; + + while (true) { + if (!output->Next(&out, &out_size)) { + return false; + } + + if (in_size <= out_size) { + memcpy(out, in, in_size); + output->BackUp(out_size - in_size); + return true; + } + + memcpy(out, in, out_size); + in += out_size; + in_size -= out_size; + } +} + +int IoTest::ReadFromInput(ZeroCopyInputStream* input, void* data, int size) { + uint8* out = reinterpret_cast(data); + int out_size = size; + + const void* in; + int in_size = 0; + + while (true) { + if (!input->Next(&in, &in_size)) { + return size - out_size; + } + + if (out_size <= in_size) { + memcpy(out, in, out_size); + input->BackUp(in_size - out_size); + return size; // Copied all of it. + } + + memcpy(out, in, in_size); + out += in_size; + out_size -= in_size; + } +} + +void IoTest::WriteString(ZeroCopyOutputStream* output, const char* str) { + EXPECT_TRUE(WriteToOutput(output, str, strlen(str))); +} + +void IoTest::ReadString(ZeroCopyInputStream* input, const char* str) { + int length = strlen(str); + scoped_array buffer(new char[length + 1]); + buffer[length] = '\0'; + EXPECT_EQ(ReadFromInput(input, buffer.get(), length), length); + EXPECT_STREQ(str, buffer.get()); +} + +int IoTest::WriteStuff(ZeroCopyOutputStream* output) { + WriteString(output, "Hello world!\n"); + WriteString(output, "Some te"); + WriteString(output, "xt. Blah blah."); + WriteString(output, "abcdefg"); + WriteString(output, "01234567890123456789"); + WriteString(output, "foobar"); + + EXPECT_EQ(output->ByteCount(), 68); + + int result = output->ByteCount(); + return result; +} + +// Reads text from an input stream and expects it to match what WriteStuff() +// writes. +void IoTest::ReadStuff(ZeroCopyInputStream* input) { + ReadString(input, "Hello world!\n"); + ReadString(input, "Some text. "); + ReadString(input, "Blah "); + ReadString(input, "blah."); + ReadString(input, "abcdefg"); + EXPECT_TRUE(input->Skip(20)); + ReadString(input, "foo"); + ReadString(input, "bar"); + + EXPECT_EQ(input->ByteCount(), 68); + + uint8 byte; + EXPECT_EQ(ReadFromInput(input, &byte, 1), 0); +} + +// =================================================================== + +TEST_F(IoTest, ArrayIo) { + const int kBufferSize = 256; + uint8 buffer[kBufferSize]; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + int size; + { + ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]); + size = WriteStuff(&output); + } + { + ArrayInputStream input(buffer, size, kBlockSizes[j]); + ReadStuff(&input); + } + } + } +} + +// There is no string input, only string output. Also, it doesn't support +// explicit block sizes. So, we'll only run one test and we'll use +// ArrayInput to read back the results. +TEST_F(IoTest, StringIo) { + string str; + { + StringOutputStream output(&str); + WriteStuff(&output); + } + { + ArrayInputStream input(str.data(), str.size()); + ReadStuff(&input); + } +} + + +// To test files, we create a temporary file, write, read, truncate, repeat. +TEST_F(IoTest, FileIo) { + string filename = TestTempDir() + "/zero_copy_stream_test_file"; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + // Make a temporary file. + int file = + open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777); + ASSERT_GE(file, 0); + + { + FileOutputStream output(file, kBlockSizes[i]); + WriteStuff(&output); + EXPECT_EQ(0, output.GetErrno()); + } + + // Rewind. + ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1); + + { + FileInputStream input(file, kBlockSizes[j]); + ReadStuff(&input); + EXPECT_EQ(0, input.GetErrno()); + } + + close(file); + } + } +} + +// MSVC raises various debugging exceptions if we try to use a file +// descriptor of -1, defeating our tests below. This class will disable +// these debug assertions while in scope. +class MsvcDebugDisabler { + public: +#ifdef _MSC_VER + MsvcDebugDisabler() { + old_handler_ = _set_invalid_parameter_handler(MyHandler); + old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0); + } + ~MsvcDebugDisabler() { + old_handler_ = _set_invalid_parameter_handler(old_handler_); + old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_); + } + + static void MyHandler(const wchar_t *expr, + const wchar_t *func, + const wchar_t *file, + unsigned int line, + uintptr_t pReserved) { + // do nothing + } + + _invalid_parameter_handler old_handler_; + int old_mode_; +#else + // Dummy constructor and destructor to ensure that GCC doesn't complain + // that debug_disabler is an unused variable. + MsvcDebugDisabler() {} + ~MsvcDebugDisabler() {} +#endif +}; + +// Test that FileInputStreams report errors correctly. +TEST_F(IoTest, FileReadError) { + MsvcDebugDisabler debug_disabler; + + // -1 = invalid file descriptor. + FileInputStream input(-1); + + const void* buffer; + int size; + EXPECT_FALSE(input.Next(&buffer, &size)); + EXPECT_EQ(EBADF, input.GetErrno()); +} + +// Test that FileOutputStreams report errors correctly. +TEST_F(IoTest, FileWriteError) { + MsvcDebugDisabler debug_disabler; + + // -1 = invalid file descriptor. + FileOutputStream input(-1); + + void* buffer; + int size; + + // The first call to Next() succeeds because it doesn't have anything to + // write yet. + EXPECT_TRUE(input.Next(&buffer, &size)); + + // Second call fails. + EXPECT_FALSE(input.Next(&buffer, &size)); + + EXPECT_EQ(EBADF, input.GetErrno()); +} + +// Pipes are not seekable, so File{Input,Output}Stream ends up doing some +// different things to handle them. We'll test by writing to a pipe and +// reading back from it. +TEST_F(IoTest, PipeIo) { + int files[2]; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + // Need to create a new pipe each time because ReadStuff() expects + // to see EOF at the end. + ASSERT_EQ(pipe(files), 0); + + { + FileOutputStream output(files[1], kBlockSizes[i]); + WriteStuff(&output); + EXPECT_EQ(0, output.GetErrno()); + } + close(files[1]); // Send EOF. + + { + FileInputStream input(files[0], kBlockSizes[j]); + ReadStuff(&input); + EXPECT_EQ(0, input.GetErrno()); + } + close(files[0]); + } + } +} + +// Test using C++ iostreams. +TEST_F(IoTest, IostreamIo) { + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + stringstream stream; + + { + OstreamOutputStream output(&stream, kBlockSizes[i]); + WriteStuff(&output); + EXPECT_FALSE(stream.fail()); + } + + { + IstreamInputStream input(&stream, kBlockSizes[j]); + ReadStuff(&input); + EXPECT_TRUE(stream.eof()); + } + } + } +} + +// To test ConcatenatingInputStream, we create several ArrayInputStreams +// covering a buffer and then concatenate them. +TEST_F(IoTest, ConcatenatingInputStream) { + const int kBufferSize = 256; + uint8 buffer[kBufferSize]; + + // Fill the buffer. + ArrayOutputStream output(buffer, kBufferSize); + WriteStuff(&output); + + // Now split it up into multiple streams of varying sizes. + ASSERT_EQ(68, output.ByteCount()); // Test depends on this. + ArrayInputStream input1(buffer , 12); + ArrayInputStream input2(buffer + 12, 7); + ArrayInputStream input3(buffer + 19, 6); + ArrayInputStream input4(buffer + 25, 15); + ArrayInputStream input5(buffer + 40, 0); + // Note: We want to make sure we have a stream boundary somewhere between + // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff(). This + // tests that a bug that existed in the original code for Skip() is fixed. + ArrayInputStream input6(buffer + 40, 10); + ArrayInputStream input7(buffer + 50, 18); // Total = 68 bytes. + + ZeroCopyInputStream* streams[] = + {&input1, &input2, &input3, &input4, &input5, &input6, &input7}; + + // Create the concatenating stream and read. + ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams)); + ReadStuff(&input); +} + +// To test LimitingInputStream, we write our golden text to a buffer, then +// create an ArrayInputStream that contains the whole buffer (not just the +// bytes written), then use a LimitingInputStream to limit it just to the +// bytes written. +TEST_F(IoTest, LimitingInputStream) { + const int kBufferSize = 256; + uint8 buffer[kBufferSize]; + + // Fill the buffer. + ArrayOutputStream output(buffer, kBufferSize); + WriteStuff(&output); + + // Set up input. + ArrayInputStream array_input(buffer, kBufferSize); + LimitingInputStream input(&array_input, output.ByteCount()); + + ReadStuff(&input); +} + +// Check that a zero-size array doesn't confuse the code. +TEST(ZeroSizeArray, Input) { + ArrayInputStream input(NULL, 0); + const void* data; + int size; + EXPECT_FALSE(input.Next(&data, &size)); +} + +TEST(ZeroSizeArray, Output) { + ArrayOutputStream output(NULL, 0); + void* data; + int size; + EXPECT_FALSE(output.Next(&data, &size)); +} + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc new file mode 100644 index 00000000..f740ef18 --- /dev/null +++ b/src/google/protobuf/message.cc @@ -0,0 +1,345 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +using internal::WireFormat; +using internal::ReflectionOps; + +static string InitializationErrorMessage(const char* action, + const Message& message) { + return strings::Substitute( + "Can't $0 message of type \"$1\" because it is missing required " + "fields: $2", + action, message.GetDescriptor()->full_name(), + message.InitializationErrorString()); +} + +Message::~Message() {} +Message::Reflection::~Reflection() {} + +void Message::MergeFrom(const Message& from) { + const Descriptor* descriptor = GetDescriptor(); + GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor) + << ": Tried to merge from a message with a different type. " + "to: " << descriptor->full_name() << ", " + "from:" << from.GetDescriptor()->full_name(); + ReflectionOps::Merge(descriptor, *from.GetReflection(), GetReflection()); +} + +void Message::CopyFrom(const Message& from) { + const Descriptor* descriptor = GetDescriptor(); + GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor) + << ": Tried to copy from a message with a different type." + "to: " << descriptor->full_name() << ", " + "from:" << from.GetDescriptor()->full_name(); + ReflectionOps::Copy(descriptor, *from.GetReflection(), GetReflection()); +} + +void Message::Clear() { + ReflectionOps::Clear(GetDescriptor(), GetReflection()); +} + +bool Message::IsInitialized() const { + return ReflectionOps::IsInitialized(GetDescriptor(), *GetReflection()); +} + +void Message::FindInitializationErrors(vector* errors) const { + return ReflectionOps::FindInitializationErrors( + GetDescriptor(), *GetReflection(), "", errors); +} + +string Message::InitializationErrorString() const { + vector errors; + FindInitializationErrors(&errors); + return JoinStrings(errors, ", "); +} + +void Message::CheckInitialized() const { + GOOGLE_CHECK(IsInitialized()) + << "Message of type \"" << GetDescriptor()->full_name() + << "\" is missing required fields: " << InitializationErrorString(); +} + +void Message::DiscardUnknownFields() { + return ReflectionOps::DiscardUnknownFields(GetDescriptor(), GetReflection()); +} + +bool Message::MergePartialFromCodedStream(io::CodedInputStream* input) { + return WireFormat::ParseAndMergePartial( + GetDescriptor(), input, GetReflection()); +} + +bool Message::MergeFromCodedStream(io::CodedInputStream* input) { + if (!MergePartialFromCodedStream(input)) return false; + if (!IsInitialized()) { + GOOGLE_LOG(ERROR) << InitializationErrorMessage("parse", *this); + return false; + } + return true; +} + +bool Message::ParseFromCodedStream(io::CodedInputStream* input) { + Clear(); + return MergeFromCodedStream(input); +} + +bool Message::ParsePartialFromCodedStream(io::CodedInputStream* input) { + Clear(); + return MergePartialFromCodedStream(input); +} + +bool Message::ParseFromZeroCopyStream(io::ZeroCopyInputStream* input) { + io::CodedInputStream decoder(input); + return ParseFromCodedStream(&decoder) && decoder.ConsumedEntireMessage(); +} + +bool Message::ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input) { + io::CodedInputStream decoder(input); + return ParsePartialFromCodedStream(&decoder) && + decoder.ConsumedEntireMessage(); +} + +bool Message::ParseFromString(const string& data) { + io::ArrayInputStream input(data.data(), data.size()); + return ParseFromZeroCopyStream(&input); +} + +bool Message::ParsePartialFromString(const string& data) { + io::ArrayInputStream input(data.data(), data.size()); + return ParsePartialFromZeroCopyStream(&input); +} + +bool Message::ParseFromArray(const void* data, int size) { + io::ArrayInputStream input(data, size); + return ParseFromZeroCopyStream(&input); +} + +bool Message::ParsePartialFromArray(const void* data, int size) { + io::ArrayInputStream input(data, size); + return ParsePartialFromZeroCopyStream(&input); +} + +bool Message::ParseFromFileDescriptor(int file_descriptor) { + io::FileInputStream input(file_descriptor); + return ParseFromZeroCopyStream(&input) && input.GetErrno() == 0; +} + +bool Message::ParsePartialFromFileDescriptor(int file_descriptor) { + io::FileInputStream input(file_descriptor); + return ParsePartialFromZeroCopyStream(&input) && input.GetErrno() == 0; +} + +bool Message::ParseFromIstream(istream* input) { + io::IstreamInputStream zero_copy_input(input); + return ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); +} + +bool Message::ParsePartialFromIstream(istream* input) { + io::IstreamInputStream zero_copy_input(input); + return ParsePartialFromZeroCopyStream(&zero_copy_input) && input->eof(); +} + + + +bool Message::SerializeWithCachedSizes( + io::CodedOutputStream* output) const { + return WireFormat::SerializeWithCachedSizes( + GetDescriptor(), GetReflection(), GetCachedSize(), output); +} + +int Message::ByteSize() const { + int size = WireFormat::ByteSize(GetDescriptor(), GetReflection()); + SetCachedSize(size); + return size; +} + +void Message::SetCachedSize(int size) const { + GOOGLE_LOG(FATAL) << "Message class \"" << GetDescriptor()->full_name() + << "\" implements neither SetCachedSize() nor ByteSize(). " + "Must implement one or the other."; +} + +bool Message::SerializeToCodedStream(io::CodedOutputStream* output) const { + GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this); + return SerializePartialToCodedStream(output); +} + +bool Message::SerializePartialToCodedStream( + io::CodedOutputStream* output) const { + ByteSize(); // Force size to be cached. + if (!SerializeWithCachedSizes(output)) return false; + return true; +} + +bool Message::SerializeToZeroCopyStream( + io::ZeroCopyOutputStream* output) const { + io::CodedOutputStream encoder(output); + return SerializeToCodedStream(&encoder); +} + +bool Message::SerializePartialToZeroCopyStream( + io::ZeroCopyOutputStream* output) const { + io::CodedOutputStream encoder(output); + return SerializePartialToCodedStream(&encoder); +} + +bool Message::AppendToString(string* output) const { + GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this); + return AppendPartialToString(output); +} + +bool Message::AppendPartialToString(string* output) const { + // For efficiency, we'd like to reserve the exact amount of space we need + // in the string. + int total_size = output->size() + ByteSize(); + output->reserve(total_size); + + io::StringOutputStream output_stream(output); + + { + io::CodedOutputStream encoder(&output_stream); + if (!SerializeWithCachedSizes(&encoder)) return false; + } + + GOOGLE_CHECK_EQ(output_stream.ByteCount(), total_size); + return true; +} + +bool Message::SerializeToString(string* output) const { + output->clear(); + return AppendToString(output); +} + +bool Message::SerializePartialToString(string* output) const { + output->clear(); + return AppendPartialToString(output); +} + +bool Message::SerializeToArray(void* data, int size) const { + io::ArrayOutputStream output_stream(data, size); + return SerializeToZeroCopyStream(&output_stream); +} + +bool Message::SerializePartialToArray(void* data, int size) const { + io::ArrayOutputStream output_stream(data, size); + return SerializePartialToZeroCopyStream(&output_stream); +} + +bool Message::SerializeToFileDescriptor(int file_descriptor) const { + io::FileOutputStream output(file_descriptor); + return SerializeToZeroCopyStream(&output); +} + +bool Message::SerializePartialToFileDescriptor(int file_descriptor) const { + io::FileOutputStream output(file_descriptor); + return SerializePartialToZeroCopyStream(&output); +} + +bool Message::SerializeToOstream(ostream* output) const { + io::OstreamOutputStream zero_copy_output(output); + return SerializeToZeroCopyStream(&zero_copy_output); +} + +bool Message::SerializePartialToOstream(ostream* output) const { + io::OstreamOutputStream zero_copy_output(output); + return SerializePartialToZeroCopyStream(&zero_copy_output); +} + + +// =================================================================== +// MessageFactory + +MessageFactory::~MessageFactory() {} + +namespace { + +class GeneratedMessageFactory : public MessageFactory { + public: + GeneratedMessageFactory(); + ~GeneratedMessageFactory(); + + static GeneratedMessageFactory* singleton(); + + void RegisterType(const Descriptor* descriptor, const Message* prototype); + + // implements MessageFactory --------------------------------------- + const Message* GetPrototype(const Descriptor* type); + + private: + hash_map type_map_; +}; + +GeneratedMessageFactory::GeneratedMessageFactory() {} +GeneratedMessageFactory::~GeneratedMessageFactory() {} + +GeneratedMessageFactory* GeneratedMessageFactory::singleton() { + // No need for thread-safety here because this will be called at static + // initialization time. (And GCC4 makes this thread-safe anyway.) + static GeneratedMessageFactory singleton; + return &singleton; +} + +void GeneratedMessageFactory::RegisterType(const Descriptor* descriptor, + const Message* prototype) { + GOOGLE_DCHECK_EQ(descriptor->file()->pool(), DescriptorPool::generated_pool()) + << "Tried to register a non-generated type with the generated " + "type registry."; + + if (!InsertIfNotPresent(&type_map_, descriptor, prototype)) { + GOOGLE_LOG(DFATAL) << "Type is already registered: " << descriptor->full_name(); + } +} + +const Message* GeneratedMessageFactory::GetPrototype(const Descriptor* type) { + return FindPtrOrNull(type_map_, type); +} + +} // namespace + +MessageFactory* MessageFactory::generated_factory() { + return GeneratedMessageFactory::singleton(); +} + +void MessageFactory::InternalRegisterGeneratedMessage( + const Descriptor* descriptor, const Message* prototype) { + GeneratedMessageFactory::singleton()->RegisterType(descriptor, prototype); +} + + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h new file mode 100644 index 00000000..2c2cf5bd --- /dev/null +++ b/src/google/protobuf/message.h @@ -0,0 +1,624 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains the abstract interface for all protocol messages. +// Although it's possible to implement this interface manually, most users +// will use the protocol compiler to generate implementations. +// +// Example usage: +// +// Say you have a message defined as: +// +// message Foo { +// optional string text = 1; +// repeated int32 numbers = 2; +// } +// +// Then, if you used the protocol compiler to generate a class from the above +// definition, you could use it like so: +// +// string data; // Will store a serialized version of the message. +// +// { +// // Create a message and serialize it. +// Foo foo; +// foo.set_text("Hello World!"); +// foo.add_numbers(1); +// foo.add_numbers(5); +// foo.add_numbers(42); +// +// foo.SerializeToString(&data); +// } +// +// { +// // Parse the serialized message and check that it contains the +// // correct data. +// Foo foo; +// foo.ParseFromString(data); +// +// assert(foo.text() == "Hello World!"); +// assert(foo.numbers_size() == 3); +// assert(foo.numbers(0) == 1); +// assert(foo.numbers(1) == 5); +// assert(foo.numbers(2) == 42); +// } +// +// { +// // Same as the last block, but do it dynamically via the Message +// // reflection interface. +// Message* foo = new Foo; +// Descriptor* descriptor = foo->GetDescriptor(); +// +// // Get the descriptors for the fields we're interested in and verify +// // their types. +// FieldDescriptor* text_field = descriptor->FindFieldByName("text"); +// assert(text_field != NULL); +// assert(text_field->type() == FieldDescriptor::TYPE_STRING); +// assert(text_field->label() == FieldDescriptor::TYPE_OPTIONAL); +// FieldDescriptor* numbers_field = descriptor->FindFieldByName("numbers"); +// assert(numbers_field != NULL); +// assert(numbers_field->type() == FieldDescriptor::TYPE_INT32); +// assert(numbers_field->label() == FieldDescriptor::TYPE_REPEATED); +// +// // Parse the message. +// foo->ParseFromString(data); +// +// // Use the reflection interface to examine the contents. +// Message::Reflection* reflection = foo->GetReflection(); +// assert(reflection->GetString(text_field) == "Hello World!"); +// assert(reflection->CountField(numbers_field) == 3); +// assert(reflection->GetInt32(numbers_field, 0) == 1); +// assert(reflection->GetInt32(numbers_field, 1) == 5); +// assert(reflection->GetInt32(numbers_field, 2) == 42); +// +// delete foo; +// } + +#ifndef GOOGLE_PROTOBUF_MESSAGE_H__ +#define GOOGLE_PROTOBUF_MESSAGE_H__ + +#include +#include +#include +#include + +namespace google { + +namespace protobuf { + +// Defined in this file. +class Message; + +// Defined in other files. +class Descriptor; // descriptor.h +class FieldDescriptor; // descriptor.h +class EnumValueDescriptor; // descriptor.h +namespace io { + class ZeroCopyInputStream; // zero_copy_stream.h + class ZeroCopyOutputStream; // zero_copy_stream.h + class CodedInputStream; // coded_stream.h + class CodedOutputStream; // coded_stream.h +} +class UnknownFieldSet; // unknown_field_set.h + +// Abstract interface for protocol messages. +// +// The methods of this class that are virtual but not pure-virtual have +// default implementations based on reflection. Message classes which are +// optimized for speed will want to override these with faster implementations, +// but classes optimized for code size may be happy with keeping them. See +// the optimize_for option in descriptor.proto. +class LIBPROTOBUF_EXPORT Message { + public: + inline Message() {} + virtual ~Message(); + + // Basic Operations ------------------------------------------------ + + // Construct a new instance of the same type. Ownership is passed to the + // caller. + virtual Message* New() const = 0; + + // Make this message into a copy of the given message. The given message + // must have the same descriptor, but need not necessarily be the same class. + // By default this is just implemented as "Clear(); MergeFrom(from);". + virtual void CopyFrom(const Message& from); + + // Merge the fields from the given message into this message. Singular + // fields will be overwritten, except for embedded messages which will + // be merged. Repeated fields will be concatenated. The given message + // must be of the same type as this message (i.e. the exact same class). + virtual void MergeFrom(const Message& from); + + // Clear all fields of the message and set them to their default values. + // Clear() avoids freeing memory, assuming that any memory allocated + // to hold parts of the message will be needed again to hold the next + // message. If you actually want to free the memory used by a Message, + // you must delete it. + virtual void Clear(); + + // Quickly check if all required fields have values set. + virtual bool IsInitialized() const; + + // Verifies that IsInitialized() returns true. GOOGLE_CHECK-fails otherwise, with + // a nice error message. + void CheckInitialized() const; + + // Slowly build a list of all required fields that are not set. + // This is much, much slower than IsInitialized() as it is implemented + // purely via reflection. Generally, you should not call this unless you + // have already determined that an error exists by calling IsInitialized(). + void FindInitializationErrors(vector* errors) const; + + // Like FindInitializationErrors, but joins all the strings, delimited by + // commas, and returns them. + string InitializationErrorString() const; + + // Clears all unknown fields from this message and all embedded messages. + // Normally, if unknown tag numbers are encountered when parsing a message, + // the tag and value are stored in the message's UnknownFieldSet and + // then written back out when the message is serialized. This allows servers + // which simply route messages to other servers to pass through messages + // that have new field definitions which they don't yet know about. However, + // this behavior can have security implications. To avoid it, call this + // method after parsing. + // + // See Reflection::GetUnknownFields() for more on unknown fields. + virtual void DiscardUnknownFields(); + + // Debugging ------------------------------------------------------- + + // Generates a human readable form of this message, useful for debugging + // and other purposes. + string DebugString() const; + // Like DebugString(), but with less whitespace. + string ShortDebugString() const; + // Convenience function useful in GDB. Prints DebugString() to stdout. + void PrintDebugString() const; + + // Parsing --------------------------------------------------------- + // Methods for parsing in protocol buffer format. Most of these are + // just simple wrappers around MergeFromCodedStream(). + + // Fill the message with a protocol buffer parsed from the given input + // stream. Returns false on a read error or if the input is in the + // wrong format. + bool ParseFromCodedStream(io::CodedInputStream* input); + // Like ParseFromCodedStream(), but accepts messages that are missing + // required fields. + bool ParsePartialFromCodedStream(io::CodedInputStream* input); + // Read a protocol buffer from the given zero-copy input stream. If + // successful, the entire input will be consumed. + bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input); + // Like ParseFromZeroCopyStream(), but accepts messages that are missing + // required fields. + bool ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input); + // Parse a protocol buffer contained in a string. + bool ParseFromString(const string& data); + // Like ParseFromString(), but accepts messages that are missing + // required fields. + bool ParsePartialFromString(const string& data); + // Parse a protocol buffer contained in an array of bytes. + bool ParseFromArray(const void* data, int size); + // Like ParseFromArray(), but accepts messages that are missing + // required fields. + bool ParsePartialFromArray(const void* data, int size); + + // Parse a protocol buffer from a file descriptor. If successful, the entire + // input will be consumed. + bool ParseFromFileDescriptor(int file_descriptor); + // Like ParseFromFileDescriptor(), but accepts messages that are missing + // required fields. + bool ParsePartialFromFileDescriptor(int file_descriptor); + // Parse a protocol buffer from a C++ istream. If successful, the entire + // input will be consumed. + bool ParseFromIstream(istream* input); + // Like ParseFromIstream(), but accepts messages that are missing + // required fields. + bool ParsePartialFromIstream(istream* input); + + + // Reads a protocol buffer from the stream and merges it into this + // Message. Singular fields read from the input overwrite what is + // already in the Message and repeated fields are appended to those + // already present. + // + // It is the responsibility of the caller to call input->LastTagWas() + // (for groups) or input->ConsumedEntireMessage() (for non-groups) after + // this returns to verify that the message's end was delimited correctly. + // + // ParsefromCodedStream() is implemented as Clear() followed by + // MergeFromCodedStream(). + bool MergeFromCodedStream(io::CodedInputStream* input); + + // Like MergeFromCodedStream(), but succeeds even if required fields are + // missing in the input. + // + // MergeFromCodedStream() is just implemented as MergePartialFromCodedStream() + // followed by IsInitialized(). + virtual bool MergePartialFromCodedStream(io::CodedInputStream* input); + + // Serialization --------------------------------------------------- + // Methods for serializing in protocol buffer format. Most of these + // are just simple wrappers around ByteSize() and SerializeWithCachedSizes(). + + // Write a protocol buffer of this message to the given output. Returns + // false on a write error. If the message is missing required fields, + // this may GOOGLE_CHECK-fail. + bool SerializeToCodedStream(io::CodedOutputStream* output) const; + // Like SerializeToCodedStream(), but allows missing required fields. + bool SerializePartialToCodedStream(io::CodedOutputStream* output) const; + // Write the message to the given zero-copy output stream. All required + // fields must be set. + bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const; + // Like SerializeToZeroCopyStream(), but allows missing required fields. + bool SerializePartialToZeroCopyStream(io::ZeroCopyOutputStream* output) const; + // Serialize the message and store it in the given string. All required + // fields must be set. + bool SerializeToString(string* output) const; + // Like SerializeToString(), but allows missing required fields. + bool SerializePartialToString(string* output) const; + // Serialize the message and store it in the given byte array. All required + // fields must be set. + bool SerializeToArray(void* data, int size) const; + // Like SerializeToArray(), but allows missing required fields. + bool SerializePartialToArray(void* data, int size) const; + + // Serialize the message and write it to the given file descriptor. All + // required fields must be set. + bool SerializeToFileDescriptor(int file_descriptor) const; + // Like SerializeToFileDescriptor(), but allows missing required fields. + bool SerializePartialToFileDescriptor(int file_descriptor) const; + // Serialize the message and write it to the given C++ ostream. All + // required fields must be set. + bool SerializeToOstream(ostream* output) const; + // Like SerializeToOstream(), but allows missing required fields. + bool SerializePartialToOstream(ostream* output) const; + + + // Like SerializeToString(), but appends to the data to the string's existing + // contents. All required fields must be set. + bool AppendToString(string* output) const; + // Like AppendToString(), but allows missing required fields. + bool AppendPartialToString(string* output) const; + + // Computes the serialized size of the message. This recursively calls + // ByteSize() on all embedded messages. If a subclass does not override + // this, it MUST override SetCachedSize(). + virtual int ByteSize() const; + + // Serializes the message without recomputing the size. The message must + // not have changed since the last call to ByteSize(); if it has, the results + // are undefined. + virtual bool SerializeWithCachedSizes(io::CodedOutputStream* output) const; + + // Returns the result of the last call to ByteSize(). An embedded message's + // size is needed both to serialize it (because embedded messages are + // length-delimited) and to compute the outer message's size. Caching + // the size avoids computing it multiple times. + // + // ByteSize() does not automatically use the cached size when available + // because this would require invalidating it every time the message was + // modified, which would be too hard and expensive. (E.g. if a deeply-nested + // sub-message is changed, all of its parents' cached sizes would need to be + // invalidated, which is too much work for an otherwise inlined setter + // method.) + virtual int GetCachedSize() const = 0; + + private: + // This is called only by the default implementation of ByteSize(), to + // update the cached size. If you override ByteSize(), you do not need + // to override this. If you do not override ByteSize(), you MUST override + // this; the default implementation will crash. + // + // The method is private because subclasses should never call it; only + // override it. Yes, C++ lets you do that. Crazy, huh? + virtual void SetCachedSize(int size) const; + + public: + + // Introspection --------------------------------------------------- + + class Reflection; // Defined below. + + // Get a Descriptor for this message's type. This describes what + // fields the message contains, the types of those fields, etc. + virtual const Descriptor* GetDescriptor() const = 0; + + // Get the Reflection interface for this Message, which can be used to + // read and modify the fields of the Message dynamically (in other words, + // without knowing the message type at compile time). This object remains + // property of the Message. + virtual const Reflection* GetReflection() const = 0; + + // Get the Reflection interface for this Message, which can be used to + // read and modify the fields of the Message dynamically (in other words, + // without knowing the message type at compile time). This object remains + // property of the Message. + virtual Reflection* GetReflection() = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Message); +}; + +// This interface contains methods that can be used to dynamically access +// and modify the fields of a protocol message. Their semantics are +// similar to the accessors the protocol compiler generates. +// +// To get the Reflection for a given Message, call Message::GetReflection(). +// +// This interface is separate from Message only for efficiency reasons; +// the vast majority of implementations of Message will share the same +// implementation of Reflection (GeneratedMessageReflection, +// defined in generated_message.h). +// +// There are several ways that these methods can be used incorrectly. For +// example, any of the following conditions will lead to undefined +// results (probably assertion failures): +// - The FieldDescriptor is not a field of this message type. +// - The method called is not appropriate for the field's type. For +// each field type in FieldDescriptor::TYPE_*, there is only one +// Get*() method, one Set*() method, and one Add*() method that is +// valid for that type. It should be obvious which (except maybe +// for TYPE_BYTES, which are represented using strings in C++). +// - A Get*() or Set*() method for singular fields is called on a repeated +// field. +// - GetRepeated*(), SetRepeated*(), or Add*() is called on a non-repeated +// field. +// +// You might wonder why there is not any abstract representation for a field +// of arbitrary type. E.g., why isn't there just a "GetField()" method that +// returns "const Field&", where "Field" is some class with accessors like +// "GetInt32Value()". The problem is that someone would have to deal with +// allocating these Field objects. For generated message classes, having to +// allocate space for an additional object to wrap every field would at least +// double the message's memory footprint, probably worse. Allocating the +// objects on-demand, on the other hand, would be expensive and prone to +// memory leaks. So, instead we ended up with this flat interface. +// +// TODO(kenton): Create a utility class which callers can use to read and +// write fields from a Reflection without paying attention to the type. +class LIBPROTOBUF_EXPORT Message::Reflection { + public: + inline Reflection() {} + virtual ~Reflection(); + + // Get the UnknownFieldSet for the message. This contains fields which + // were seen when the Message was parsed but were not recognized according + // to the Message's definition. + virtual const UnknownFieldSet& GetUnknownFields() const = 0; + // Get a mutable pointer to the UnknownFieldSet for the message. This + // contains fields which were seen when the Message was parsed but were not + // recognized according to the Message's definition. + virtual UnknownFieldSet* MutableUnknownFields() = 0; + + // Check if the given non-repeated field is set. + virtual bool HasField(const FieldDescriptor* field) const = 0; + + // Get the number of elements of a repeated field. + virtual int FieldSize(const FieldDescriptor* field) const = 0; + + // Clear the value of a field, so that HasField() returns false or + // FieldSize() returns zero. + virtual void ClearField(const FieldDescriptor* field) = 0; + + // List all fields of the message which are currently set. This includes + // extensions. Singular fields will only be listed if HasField(field) would + // return true and repeated fields will only be listed if FieldSize(field) + // would return non-zero. Fields (both normal fields and extension fields) + // will be listed ordered by field number. + virtual void ListFields(vector* output) const = 0; + + // Singular field getters ------------------------------------------ + // These get the value of a non-repeated field. They return the default + // value for fields that aren't set. + + virtual int32 GetInt32 (const FieldDescriptor* field) const = 0; + virtual int64 GetInt64 (const FieldDescriptor* field) const = 0; + virtual uint32 GetUInt32(const FieldDescriptor* field) const = 0; + virtual uint64 GetUInt64(const FieldDescriptor* field) const = 0; + virtual float GetFloat (const FieldDescriptor* field) const = 0; + virtual double GetDouble(const FieldDescriptor* field) const = 0; + virtual bool GetBool (const FieldDescriptor* field) const = 0; + virtual string GetString(const FieldDescriptor* field) const = 0; + virtual const EnumValueDescriptor* GetEnum( + const FieldDescriptor* field) const = 0; + virtual const Message& GetMessage(const FieldDescriptor* field) const = 0; + + // Get a string value without copying, if possible. + // + // GetString() necessarily returns a copy of the string. This can be + // inefficient when the string is already stored in a string object in the + // underlying message. GetStringReference() will return a reference to the + // underlying string in this case. Otherwise, it will copy the string into + // *scratch and return that. + // + // Note: It is perfectly reasonable and useful to write code like: + // str = reflection->GetStringReference(field, &str); + // This line would ensure that only one copy of the string is made + // regardless of the field's underlying representation. When initializing + // a newly-constructed string, though, it's just as fast and more readable + // to use code like: + // string str = reflection->GetString(field); + virtual const string& GetStringReference(const FieldDescriptor* field, + string* scratch) const = 0; + + + // Singular field mutators ----------------------------------------- + // These mutate the value of a non-repeated field. + + virtual void SetInt32 (const FieldDescriptor* field, int32 value) = 0; + virtual void SetInt64 (const FieldDescriptor* field, int64 value) = 0; + virtual void SetUInt32(const FieldDescriptor* field, uint32 value) = 0; + virtual void SetUInt64(const FieldDescriptor* field, uint64 value) = 0; + virtual void SetFloat (const FieldDescriptor* field, float value) = 0; + virtual void SetDouble(const FieldDescriptor* field, double value) = 0; + virtual void SetBool (const FieldDescriptor* field, bool value) = 0; + virtual void SetString(const FieldDescriptor* field, const string& value) = 0; + virtual void SetEnum (const FieldDescriptor* field, + const EnumValueDescriptor* value) = 0; + // Get a mutable pointer to a field with a message type. + virtual Message* MutableMessage(const FieldDescriptor* field) = 0; + + + // Repeated field getters ------------------------------------------ + // These get the value of one element of a repeated field. + + virtual int32 GetRepeatedInt32 (const FieldDescriptor* field, + int index) const = 0; + virtual int64 GetRepeatedInt64 (const FieldDescriptor* field, + int index) const = 0; + virtual uint32 GetRepeatedUInt32(const FieldDescriptor* field, + int index) const = 0; + virtual uint64 GetRepeatedUInt64(const FieldDescriptor* field, + int index) const = 0; + virtual float GetRepeatedFloat (const FieldDescriptor* field, + int index) const = 0; + virtual double GetRepeatedDouble(const FieldDescriptor* field, + int index) const = 0; + virtual bool GetRepeatedBool (const FieldDescriptor* field, + int index) const = 0; + virtual string GetRepeatedString(const FieldDescriptor* field, + int index) const = 0; + virtual const EnumValueDescriptor* GetRepeatedEnum( + const FieldDescriptor* field, int index) const = 0; + virtual const Message& GetRepeatedMessage( + const FieldDescriptor* field, int index) const = 0; + + // See GetStringReference(), above. + virtual const string& GetRepeatedStringReference( + const FieldDescriptor* field, int index, + string* scratch) const = 0; + + + // Repeated field mutators ----------------------------------------- + // These mutate the value of one element of a repeated field. + + virtual void SetRepeatedInt32 (const FieldDescriptor* field, + int index, int32 value) = 0; + virtual void SetRepeatedInt64 (const FieldDescriptor* field, + int index, int64 value) = 0; + virtual void SetRepeatedUInt32(const FieldDescriptor* field, + int index, uint32 value) = 0; + virtual void SetRepeatedUInt64(const FieldDescriptor* field, + int index, uint64 value) = 0; + virtual void SetRepeatedFloat (const FieldDescriptor* field, + int index, float value) = 0; + virtual void SetRepeatedDouble(const FieldDescriptor* field, + int index, double value) = 0; + virtual void SetRepeatedBool (const FieldDescriptor* field, + int index, bool value) = 0; + virtual void SetRepeatedString(const FieldDescriptor* field, + int index, const string& value) = 0; + virtual void SetRepeatedEnum(const FieldDescriptor* field, + int index, const EnumValueDescriptor* value) = 0; + // Get a mutable pointer to an element of a repeated field with a message + // type. + virtual Message* MutableRepeatedMessage( + const FieldDescriptor* field, int index) = 0; + + + // Repeated field adders ------------------------------------------- + // These add an element to a repeated field. + + virtual void AddInt32 (const FieldDescriptor* field, int32 value) = 0; + virtual void AddInt64 (const FieldDescriptor* field, int64 value) = 0; + virtual void AddUInt32(const FieldDescriptor* field, uint32 value) = 0; + virtual void AddUInt64(const FieldDescriptor* field, uint64 value) = 0; + virtual void AddFloat (const FieldDescriptor* field, float value) = 0; + virtual void AddDouble(const FieldDescriptor* field, double value) = 0; + virtual void AddBool (const FieldDescriptor* field, bool value) = 0; + virtual void AddString(const FieldDescriptor* field, const string& value) = 0; + virtual void AddEnum (const FieldDescriptor* field, + const EnumValueDescriptor* value) = 0; + virtual Message* AddMessage(const FieldDescriptor* field) = 0; + + + // Extensions ------------------------------------------------------ + + // Try to find an extension of this message type by fully-qualified field + // name. Returns NULL if no extension is known for this name or number. + virtual const FieldDescriptor* FindKnownExtensionByName( + const string& name) const = 0; + + // Try to find an extension of this message type by field number. + // Returns NULL if no extension is known for this name or number. + virtual const FieldDescriptor* FindKnownExtensionByNumber( + int number) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reflection); +}; + +// Abstract interface for a factory for message objects. +class LIBPROTOBUF_EXPORT MessageFactory { + public: + inline MessageFactory() {} + virtual ~MessageFactory(); + + // Given a Descriptor, gets or constructs the default (prototype) Message + // of that type. You can then call that message's New() method to construct + // a mutable message of that type. + // + // Calling this method twice with the same Descriptor returns the same + // object. The returned object remains property of the factory. Also, any + // objects created by calling the prototype's New() method share some data + // with the prototype, so these must be destoyed before the MessageFactory + // is destroyed. + // + // The given descriptor must outlive the returned message, and hence must + // outlive the MessageFactory. + // + // Some implementations do not support all types. GetPrototype() will + // return NULL if the descriptor passed in is not supported. + // + // This method may or may not be thread-safe depending on the implementation. + // Each implementation should document its own degree thread-safety. + virtual const Message* GetPrototype(const Descriptor* type) = 0; + + // Gets a MessageFactory which supports all generated, compiled-in messages. + // In other words, for any compiled-in type FooMessage, the following is true: + // MessageFactory::generated_factory()->GetPrototype( + // FooMessage::descriptor()) == FooMessage::default_instance() + // This factory supports all types which are found in + // DescriptorPool::generated_pool(). If given a descriptor from any other + // pool, GetPrototype() will return NULL. (You can also check if a + // descriptor is for a generated message by checking if + // descriptor->file()->pool() == DescriptorPool::generated_pool().) + // + // This factory is 100% thread-safe; calling GetPrototype() does not modify + // any shared data. + // + // This factory is a singleton. The caller must not delete the object. + static MessageFactory* generated_factory(); + + // For internal use only: Registers a message type at static initialization + // time, to be placed in generated_factory(). + static void InternalRegisterGeneratedMessage(const Descriptor* descriptor, + const Message* prototype); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_MESSAGE_H__ diff --git a/src/google/protobuf/message_unittest.cc b/src/google/protobuf/message_unittest.cc new file mode 100644 index 00000000..491d3799 --- /dev/null +++ b/src/google/protobuf/message_unittest.cc @@ -0,0 +1,224 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#ifdef _MSC_VER +#include +#else +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +TEST(MessageTest, SerializeHelpers) { + // TODO(kenton): Test more helpers? They're all two-liners so it seems + // like a waste of time. + + protobuf_unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + stringstream stream; + + string str1("foo"); + string str2("bar"); + + message.SerializeToString(&str1); + message.AppendToString(&str2); + message.SerializeToOstream(&stream); + + EXPECT_EQ(str1.size() + 3, str2.size()); + EXPECT_EQ("bar", str2.substr(0, 3)); + // Don't use EXPECT_EQ because we don't want to dump raw binary data to + // stdout. + EXPECT_TRUE(str2.substr(3) == str1); + + // GCC gives some sort of error if we try to just do stream.str() == str1. + string temp = stream.str(); + EXPECT_TRUE(temp == str1); + +} + +TEST(MessageTest, ParseFromFileDescriptor) { + string filename = TestSourceDir() + + "/google/protobuf/testdata/golden_message"; + int file = open(filename.c_str(), O_RDONLY | O_BINARY); + + unittest::TestAllTypes message; + EXPECT_TRUE(message.ParseFromFileDescriptor(file)); + TestUtil::ExpectAllFieldsSet(message); + + EXPECT_GE(close(file), 0); +} + +TEST(MessageTest, ParseHelpers) { + // TODO(kenton): Test more helpers? They're all two-liners so it seems + // like a waste of time. + string data; + + { + // Set up. + protobuf_unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + message.SerializeToString(&data); + } + + { + // Test ParseFromString. + protobuf_unittest::TestAllTypes message; + EXPECT_TRUE(message.ParseFromString(data)); + TestUtil::ExpectAllFieldsSet(message); + } + + { + // Test ParseFromIstream. + protobuf_unittest::TestAllTypes message; + stringstream stream(data); + EXPECT_TRUE(message.ParseFromIstream(&stream)); + EXPECT_TRUE(stream.eof()); + TestUtil::ExpectAllFieldsSet(message); + } +} + +TEST(MessageTest, ParseFailsIfNotInitialized) { + unittest::TestRequired message; + vector errors; + + { + ScopedMemoryLog log; + EXPECT_FALSE(message.ParseFromString("")); + errors = log.GetMessages(ERROR); + } + + ASSERT_EQ(1, errors.size()); + EXPECT_EQ("Can't parse message of type \"protobuf_unittest.TestRequired\" " + "because it is missing required fields: a, b, c", + errors[0]); +} + +TEST(MessageTest, BypassInitializationCheckOnParse) { + unittest::TestRequired message; + io::ArrayInputStream raw_input(NULL, 0); + io::CodedInputStream input(&raw_input); + EXPECT_TRUE(message.MergePartialFromCodedStream(&input)); +} + +TEST(MessageTest, InitializationErrorString) { + unittest::TestRequired message; + EXPECT_EQ("a, b, c", message.InitializationErrorString()); +} + +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet. + +TEST(MessageTest, SerializeFailsIfNotInitialized) { + unittest::TestRequired message; + string data; + EXPECT_DEBUG_DEATH(EXPECT_TRUE(message.SerializeToString(&data)), + "Can't serialize message of type \"protobuf_unittest.TestRequired\" because " + "it is missing required fields: a, b, c"); +} + +TEST(MessageTest, CheckInitialized) { + unittest::TestRequired message; + EXPECT_DEATH(message.CheckInitialized(), + "Message of type \"protobuf_unittest.TestRequired\" is missing required " + "fields: a, b, c"); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(MessageTest, BypassInitializationCheckOnSerialize) { + unittest::TestRequired message; + io::ArrayOutputStream raw_output(NULL, 0); + io::CodedOutputStream output(&raw_output); + EXPECT_TRUE(message.SerializePartialToCodedStream(&output)); +} + +TEST(MessageTest, FindInitializationErrors) { + unittest::TestRequired message; + vector errors; + message.FindInitializationErrors(&errors); + ASSERT_EQ(3, errors.size()); + EXPECT_EQ("a", errors[0]); + EXPECT_EQ("b", errors[1]); + EXPECT_EQ("c", errors[2]); +} + +TEST(MessageTest, ParseFailsOnInvalidMessageEnd) { + unittest::TestAllTypes message; + + // Control case. + EXPECT_TRUE(message.ParseFromArray("", 0)); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromArray("\0", 1)); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromArray("\200", 1)); + + // The byte is an endgroup tag, but we aren't parsing a group. + EXPECT_FALSE(message.ParseFromArray("\014", 1)); +} + +TEST(MessageFactoryTest, GeneratedFactoryLookup) { + EXPECT_EQ( + MessageFactory::generated_factory()->GetPrototype( + protobuf_unittest::TestAllTypes::descriptor()), + &protobuf_unittest::TestAllTypes::default_instance()); +} + +TEST(MessageFactoryTest, GeneratedFactoryUnknownType) { + // Construct a new descriptor. + DescriptorPool pool; + FileDescriptorProto file; + file.set_name("foo.proto"); + file.add_message_type()->set_name("Foo"); + const Descriptor* descriptor = pool.BuildFile(file)->message_type(0); + + // Trying to construct it should return NULL. + EXPECT_TRUE( + MessageFactory::generated_factory()->GetPrototype(descriptor) == NULL); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/package_info.h b/src/google/protobuf/package_info.h new file mode 100644 index 00000000..0ba6e791 --- /dev/null +++ b/src/google/protobuf/package_info.h @@ -0,0 +1,50 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file exists solely to document the google::protobuf namespace. +// It is not compiled into anything, but it may be read by an automated +// documentation generator. + +namespace google { + +// Core components of the Protocol Buffers runtime library. +// +// The files in this package represent the core of the Protocol Buffer +// system. All of them are part of the libprotobuf library. +// +// A note on thread-safety: +// +// Thread-safety in the Protocol Buffer library follows a simple rule: +// unless explicitly noted otherwise, it is always safe to use an object +// from multiple threads simultaneously as long as the object is declared +// const in all threads (or, it is only used in ways that would be allowed +// if it were declared const). However, if an object is accessed in one +// thread in a way that would not be allowed if it were const, then it is +// not safe to access that object in any other thread simultaneously. +// +// Put simply, read-only access to an object can happen in multiple threads +// simultaneously, but write access can only happen in a single thread at +// a time. +// +// The implementation does contain some "const" methods which actually modify +// the object behind the scenes -- e.g., to cache results -- but in these cases +// mutex locking is used to make the access thread-safe. +namespace protobuf {} +} // namespace google diff --git a/src/google/protobuf/reflection_ops.cc b/src/google/protobuf/reflection_ops.cc new file mode 100644 index 00000000..2b263298 --- /dev/null +++ b/src/google/protobuf/reflection_ops.cc @@ -0,0 +1,241 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +void ReflectionOps::Copy(const Descriptor* descriptor, + const Message::Reflection& from, + Message::Reflection* to) { + if (&from == to) return; + Clear(descriptor, to); + Merge(descriptor, from, to); +} + +void ReflectionOps::Merge(const Descriptor* descriptor, + const Message::Reflection& from, + Message::Reflection* to) { + GOOGLE_CHECK_NE(&from, to); + vector fields; + from.ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + + if (field->is_repeated()) { + int count = from.FieldSize(field); + for (int j = 0; j < count; j++) { + switch (field->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, METHOD) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + to->Add##METHOD(field, \ + from.GetRepeated##METHOD(field, j)); \ + break; + + HANDLE_TYPE(INT32 , Int32 ); + HANDLE_TYPE(INT64 , Int64 ); + HANDLE_TYPE(UINT32, UInt32); + HANDLE_TYPE(UINT64, UInt64); + HANDLE_TYPE(FLOAT , Float ); + HANDLE_TYPE(DOUBLE, Double); + HANDLE_TYPE(BOOL , Bool ); + HANDLE_TYPE(STRING, String); + HANDLE_TYPE(ENUM , Enum ); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_MESSAGE: + to->AddMessage(field)->MergeFrom( + from.GetRepeatedMessage(field, j)); + break; + } + } + } else if (from.HasField(field)) { + switch (field->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, METHOD) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + to->Set##METHOD(field, from.Get##METHOD(field)); \ + break; + + HANDLE_TYPE(INT32 , Int32 ); + HANDLE_TYPE(INT64 , Int64 ); + HANDLE_TYPE(UINT32, UInt32); + HANDLE_TYPE(UINT64, UInt64); + HANDLE_TYPE(FLOAT , Float ); + HANDLE_TYPE(DOUBLE, Double); + HANDLE_TYPE(BOOL , Bool ); + HANDLE_TYPE(STRING, String); + HANDLE_TYPE(ENUM , Enum ); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_MESSAGE: + to->MutableMessage(field)->MergeFrom( + from.GetMessage(field)); + break; + } + } + } + + to->MutableUnknownFields()->MergeFrom(from.GetUnknownFields()); +} + +void ReflectionOps::Clear(const Descriptor* descriptor, + Message::Reflection* reflection) { + vector fields; + reflection->ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + reflection->ClearField(fields[i]); + } + + reflection->MutableUnknownFields()->Clear(); +} + +bool ReflectionOps::IsInitialized(const Descriptor* descriptor, + const Message::Reflection& reflection) { + // Check required fields of this message. + for (int i = 0; i < descriptor->field_count(); i++) { + if (descriptor->field(i)->is_required()) { + if (!reflection.HasField(descriptor->field(i))) { + return false; + } + } + } + + // Check that sub-messages are initialized. + vector fields; + reflection.ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (field->is_repeated()) { + int size = reflection.FieldSize(field); + + for (int i = 0; i < size; i++) { + if (!reflection.GetRepeatedMessage(field, i).IsInitialized()) { + return false; + } + } + } else { + if (reflection.HasField(field) && + !reflection.GetMessage(field).IsInitialized()) { + return false; + } + } + } + } + + return true; +} + +void ReflectionOps::DiscardUnknownFields( + const Descriptor* descriptor, + Message::Reflection* reflection) { + reflection->MutableUnknownFields()->Clear(); + + vector fields; + reflection->ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (field->is_repeated()) { + int size = reflection->FieldSize(field); + for (int i = 0; i < size; i++) { + reflection->MutableRepeatedMessage(field, i)->DiscardUnknownFields(); + } + } else { + if (reflection->HasField(field)) { + reflection->MutableMessage(field)->DiscardUnknownFields(); + } + } + } + } +} + +static string SubMessagePrefix(const string& prefix, + const FieldDescriptor* field, + int index) { + string result(prefix); + if (field->is_extension()) { + result.append("("); + result.append(field->full_name()); + result.append(")"); + } else { + result.append(field->name()); + } + if (index != -1) { + result.append("["); + result.append(SimpleItoa(index)); + result.append("]"); + } + result.append("."); + return result; +} + +void ReflectionOps::FindInitializationErrors( + const Descriptor* descriptor, + const Message::Reflection& reflection, + const string& prefix, + vector* errors) { + // Check required fields of this message. + for (int i = 0; i < descriptor->field_count(); i++) { + if (descriptor->field(i)->is_required()) { + if (!reflection.HasField(descriptor->field(i))) { + errors->push_back(prefix + descriptor->field(i)->name()); + } + } + } + + // Check sub-messages. + vector fields; + reflection.ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + + if (field->is_repeated()) { + int size = reflection.FieldSize(field); + + for (int i = 0; i < size; i++) { + const Message& sub_message = reflection.GetRepeatedMessage(field, i); + FindInitializationErrors(field->message_type(), + *sub_message.GetReflection(), + SubMessagePrefix(prefix, field, i), + errors); + } + } else { + if (reflection.HasField(field)) { + const Message& sub_message = reflection.GetMessage(field); + FindInitializationErrors(field->message_type(), + *sub_message.GetReflection(), + SubMessagePrefix(prefix, field, -1), + errors); + } + } + } + } +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/reflection_ops.h b/src/google/protobuf/reflection_ops.h new file mode 100644 index 00000000..4a7f76bb --- /dev/null +++ b/src/google/protobuf/reflection_ops.h @@ -0,0 +1,74 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_REFLECTION_OPS_H__ +#define GOOGLE_PROTOBUF_REFLECTION_OPS_H__ + +#include + +namespace google { +namespace protobuf { +namespace internal { + +// Basic operations that can be performed using Message::Reflection. +// These can be used as a cheap way to implement the corresponding +// methods of the Message interface, though they are likely to be +// slower than implementations tailored for the specific message type. +// +// This class should stay limited to operations needed to implement +// the Message interface. +// +// This class is really a namespace that contains only static methods. +class LIBPROTOBUF_EXPORT ReflectionOps { + public: + static void Copy(const Descriptor* descriptor, + const Message::Reflection& from, + Message::Reflection* to); + static void Merge(const Descriptor* descriptor, + const Message::Reflection& from, + Message::Reflection* to); + static void Clear(const Descriptor* descriptor, + Message::Reflection* reflection); + static bool IsInitialized(const Descriptor* descriptor, + const Message::Reflection& reflection); + static void DiscardUnknownFields(const Descriptor* descriptor, + Message::Reflection* reflection); + + // Finds all unset required fields in the message and adds their full + // paths (e.g. "foo.bar[5].baz") to *names. "prefix" will be attached to + // the front of each name. + static void FindInitializationErrors(const Descriptor* descriptor, + const Message::Reflection& reflection, + const string& prefix, + vector* errors); + + private: + // All methods are static. No need to construct. + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionOps); +}; + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_REFLECTION_OPS_H__ diff --git a/src/google/protobuf/reflection_ops_unittest.cc b/src/google/protobuf/reflection_ops_unittest.cc new file mode 100644 index 00000000..e1af2fca --- /dev/null +++ b/src/google/protobuf/reflection_ops_unittest.cc @@ -0,0 +1,421 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { +namespace { + +TEST(ReflectionOpsTest, SanityCheck) { + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + TestUtil::ExpectAllFieldsSet(message); +} + +TEST(ReflectionOpsTest, Copy) { + unittest::TestAllTypes message, message2; + + TestUtil::SetAllFields(&message); + + ReflectionOps::Copy(message.descriptor(), *message.GetReflection(), + message2.GetReflection()); + + TestUtil::ExpectAllFieldsSet(message2); + + // Copying from self should be a no-op. + ReflectionOps::Copy(message2.descriptor(), *message2.GetReflection(), + message2.GetReflection()); + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(ReflectionOpsTest, CopyExtensions) { + unittest::TestAllExtensions message, message2; + + TestUtil::SetAllExtensions(&message); + + ReflectionOps::Copy(message.descriptor(), *message.GetReflection(), + message2.GetReflection()); + + TestUtil::ExpectAllExtensionsSet(message2); +} + +TEST(ReflectionOpsTest, Merge) { + // Note: Copy is implemented in terms of Merge() so technically the Copy + // test already tested most of this. + + unittest::TestAllTypes message, message2; + + TestUtil::SetAllFields(&message); + + // This field will test merging into an empty spot. + message2.set_optional_int32(message.optional_int32()); + message.clear_optional_int32(); + + // This tests overwriting. + message2.set_optional_string(message.optional_string()); + message.set_optional_string("something else"); + + // This tests concatenating. + message2.add_repeated_int32(message.repeated_int32(1)); + int32 i = message.repeated_int32(0); + message.clear_repeated_int32(); + message.add_repeated_int32(i); + + ReflectionOps::Merge(message2.descriptor(), *message2.GetReflection(), + message.GetReflection()); + + TestUtil::ExpectAllFieldsSet(message); +} + +TEST(ReflectionOpsTest, MergeExtensions) { + // Note: Copy is implemented in terms of Merge() so technically the Copy + // test already tested most of this. + + unittest::TestAllExtensions message, message2; + + TestUtil::SetAllExtensions(&message); + + // This field will test merging into an empty spot. + message2.SetExtension(unittest::optional_int32_extension, + message.GetExtension(unittest::optional_int32_extension)); + message.ClearExtension(unittest::optional_int32_extension); + + // This tests overwriting. + message2.SetExtension(unittest::optional_string_extension, + message.GetExtension(unittest::optional_string_extension)); + message.SetExtension(unittest::optional_string_extension, "something else"); + + // This tests concatenating. + message2.AddExtension(unittest::repeated_int32_extension, + message.GetExtension(unittest::repeated_int32_extension, 1)); + int32 i = message.GetExtension(unittest::repeated_int32_extension, 0); + message.ClearExtension(unittest::repeated_int32_extension); + message.AddExtension(unittest::repeated_int32_extension, i); + + ReflectionOps::Merge(message2.descriptor(), *message2.GetReflection(), + message.GetReflection()); + + TestUtil::ExpectAllExtensionsSet(message); +} + +TEST(ReflectionOpsTest, MergeUnknown) { + // Test that the messages' UnknownFieldSets are correctly merged. + unittest::TestEmptyMessage message1, message2; + message1.mutable_unknown_fields()->AddField(1234)->add_varint(1); + message2.mutable_unknown_fields()->AddField(1234)->add_varint(2); + + ReflectionOps::Merge(unittest::TestEmptyMessage::descriptor(), + *message2.GetReflection(), + message1.GetReflection()); + + ASSERT_EQ(1, message1.unknown_fields().field_count()); + const UnknownField& field = message1.unknown_fields().field(0); + ASSERT_EQ(2, field.varint_size()); + EXPECT_EQ(1, field.varint(0)); + EXPECT_EQ(2, field.varint(1)); +} + +#ifdef GTEST_HAS_DEATH_TEST + +TEST(ReflectionOpsTest, MergeFromSelf) { + // Note: Copy is implemented in terms of Merge() so technically the Copy + // test already tested most of this. + + unittest::TestAllTypes message; + + EXPECT_DEATH( + ReflectionOps::Merge(message.descriptor(), *message.GetReflection(), + message.GetReflection()), + "&from"); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(ReflectionOpsTest, Clear) { + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + + ReflectionOps::Clear(message.descriptor(), message.GetReflection()); + + TestUtil::ExpectClear(message); + + // Check that getting embedded messages returns the objects created during + // SetAllFields() rather than default instances. + EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &message.optionalgroup()); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.optional_nested_message()); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.optional_foreign_message()); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.optional_import_message()); +} + +TEST(ReflectionOpsTest, ClearExtensions) { + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + + ReflectionOps::Clear(message.descriptor(), message.GetReflection()); + + TestUtil::ExpectExtensionsClear(message); + + // Check that getting embedded messages returns the objects created during + // SetAllExtensions() rather than default instances. + EXPECT_NE(&unittest::OptionalGroup_extension::default_instance(), + &message.GetExtension(unittest::optionalgroup_extension)); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.GetExtension(unittest::optional_nested_message_extension)); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.GetExtension( + unittest::optional_foreign_message_extension)); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.GetExtension(unittest::optional_import_message_extension)); +} + +TEST(ReflectionOpsTest, ClearUnknown) { + // Test that the message's UnknownFieldSet is correctly cleared. + unittest::TestEmptyMessage message; + message.mutable_unknown_fields()->AddField(1234)->add_varint(1); + + ReflectionOps::Clear(message.descriptor(), message.GetReflection()); + + EXPECT_EQ(0, message.unknown_fields().field_count()); +} + +TEST(ReflectionOpsTest, DiscardUnknownFields) { + unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + + // Set some unknown fields in message. + message.mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.mutable_optional_nested_message() + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.mutable_repeated_nested_message(0) + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + + EXPECT_EQ(1, message.unknown_fields().field_count()); + EXPECT_EQ(1, message.optional_nested_message() + .unknown_fields().field_count()); + EXPECT_EQ(1, message.repeated_nested_message(0) + .unknown_fields().field_count()); + + // Discard them. + ReflectionOps::DiscardUnknownFields(message.GetDescriptor(), + message.GetReflection()); + TestUtil::ExpectAllFieldsSet(message); + + EXPECT_EQ(0, message.unknown_fields().field_count()); + EXPECT_EQ(0, message.optional_nested_message() + .unknown_fields().field_count()); + EXPECT_EQ(0, message.repeated_nested_message(0) + .unknown_fields().field_count()); +} + +TEST(ReflectionOpsTest, DiscardUnknownExtensions) { + unittest::TestAllExtensions message; + TestUtil::SetAllExtensions(&message); + + // Set some unknown fields. + message.mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.MutableExtension(unittest::optional_nested_message_extension) + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.MutableExtension(unittest::repeated_nested_message_extension, 0) + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + + EXPECT_EQ(1, message.unknown_fields().field_count()); + EXPECT_EQ(1, + message.GetExtension(unittest::optional_nested_message_extension) + .unknown_fields().field_count()); + EXPECT_EQ(1, + message.GetExtension(unittest::repeated_nested_message_extension, 0) + .unknown_fields().field_count()); + + // Discard them. + ReflectionOps::DiscardUnknownFields(message.GetDescriptor(), + message.GetReflection()); + TestUtil::ExpectAllExtensionsSet(message); + + EXPECT_EQ(0, message.unknown_fields().field_count()); + EXPECT_EQ(0, + message.GetExtension(unittest::optional_nested_message_extension) + .unknown_fields().field_count()); + EXPECT_EQ(0, + message.GetExtension(unittest::repeated_nested_message_extension, 0) + .unknown_fields().field_count()); +} + +TEST(ReflectionOpsTest, IsInitialized) { + unittest::TestRequired message; + + EXPECT_FALSE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + message.set_a(1); + EXPECT_FALSE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + message.set_b(2); + EXPECT_FALSE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + message.set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); +} + +TEST(ReflectionOpsTest, ForeignIsInitialized) { + unittest::TestRequiredForeign message; + + // Starts out initialized because the foreign message is itself an optional + // field. + EXPECT_TRUE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Once we create that field, the message is no longer initialized. + message.mutable_optional_message(); + EXPECT_FALSE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Initialize it. Now we're initialized. + message.mutable_optional_message()->set_a(1); + message.mutable_optional_message()->set_b(2); + message.mutable_optional_message()->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Add a repeated version of the message. No longer initialized. + unittest::TestRequired* sub_message = message.add_repeated_message(); + EXPECT_FALSE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Initialize that repeated version. + sub_message->set_a(1); + sub_message->set_b(2); + sub_message->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); +} + +TEST(ReflectionOpsTest, ExtensionIsInitialized) { + unittest::TestAllExtensions message; + + // Starts out initialized because the foreign message is itself an optional + // field. + EXPECT_TRUE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Once we create that field, the message is no longer initialized. + message.MutableExtension(unittest::TestRequired::single); + EXPECT_FALSE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Initialize it. Now we're initialized. + message.MutableExtension(unittest::TestRequired::single)->set_a(1); + message.MutableExtension(unittest::TestRequired::single)->set_b(2); + message.MutableExtension(unittest::TestRequired::single)->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Add a repeated version of the message. No longer initialized. + message.AddExtension(unittest::TestRequired::multi); + EXPECT_FALSE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); + + // Initialize that repeated version. + message.MutableExtension(unittest::TestRequired::multi, 0)->set_a(1); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_b(2); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message.descriptor(), + *message.GetReflection())); +} + +static string FindInitializationErrors(const Message& message) { + vector errors; + ReflectionOps::FindInitializationErrors(message.GetDescriptor(), + *message.GetReflection(), + "", &errors); + return JoinStrings(errors, ","); +} + +TEST(ReflectionOpsTest, FindInitializationErrors) { + unittest::TestRequired message; + EXPECT_EQ("a,b,c", FindInitializationErrors(message)); +} + +TEST(ReflectionOpsTest, FindForeignInitializationErrors) { + unittest::TestRequiredForeign message; + message.mutable_optional_message(); + message.add_repeated_message(); + message.add_repeated_message(); + EXPECT_EQ("optional_message.a," + "optional_message.b," + "optional_message.c," + "repeated_message[0].a," + "repeated_message[0].b," + "repeated_message[0].c," + "repeated_message[1].a," + "repeated_message[1].b," + "repeated_message[1].c", + FindInitializationErrors(message)); +} + +TEST(ReflectionOpsTest, FindExtensionInitializationErrors) { + unittest::TestAllExtensions message; + message.MutableExtension(unittest::TestRequired::single); + message.AddExtension(unittest::TestRequired::multi); + message.AddExtension(unittest::TestRequired::multi); + EXPECT_EQ("(protobuf_unittest.TestRequired.single).a," + "(protobuf_unittest.TestRequired.single).b," + "(protobuf_unittest.TestRequired.single).c," + "(protobuf_unittest.TestRequired.multi)[0].a," + "(protobuf_unittest.TestRequired.multi)[0].b," + "(protobuf_unittest.TestRequired.multi)[0].c," + "(protobuf_unittest.TestRequired.multi)[1].a," + "(protobuf_unittest.TestRequired.multi)[1].b," + "(protobuf_unittest.TestRequired.multi)[1].c", + FindInitializationErrors(message)); +} + +} // namespace +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/repeated_field.cc b/src/google/protobuf/repeated_field.cc new file mode 100644 index 00000000..53a3c958 --- /dev/null +++ b/src/google/protobuf/repeated_field.cc @@ -0,0 +1,41 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +namespace google { +namespace protobuf { +namespace internal { + +GenericRepeatedField::~GenericRepeatedField() {} + +} // namespace internal + +template <> +void RepeatedPtrField::Clear() { + for (int i = 0; i < current_size_; i++) { + elements_[i]->clear(); + } + current_size_ = 0; +} + +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h new file mode 100644 index 00000000..3368e8b7 --- /dev/null +++ b/src/google/protobuf/repeated_field.h @@ -0,0 +1,782 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// RepeatedField and RepeatedPtrField are used by generated protocol message +// classes to manipulate repeated fields. These classes are very similar to +// STL's vector, but include a number of optimizations found to be useful +// specifically in the case of Protocol Buffers. RepeatedPtrField is +// particularly different from STL vector as it manages ownership of the +// pointers that it contains. +// +// Typically, clients should not need to access RepeatedField objects directly, +// but should instead use the accessor functions generated automatically by the +// protocol compiler. + +#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__ +#define GOOGLE_PROTOBUF_REPEATED_FIELD_H__ + +#include +#include +#include +#include + + +namespace google { +namespace protobuf { + +namespace internal { + +// DO NOT USE GenericRepeatedField; it should be considered a private detail +// of RepeatedField/RepeatedPtrField that may be removed in the future. +// GeneratedMessageReflection needs to manipulate repeated fields in a +// generic way, so we have them implement this interface. This should ONLY +// be used by GeneratedMessageReflection. This would normally be very bad +// design but GeneratedMessageReflection is a big efficiency hack anyway. +// +// TODO(kenton): Implement something like Jeff's ProtoVoidPtrArray change. +// Then, get rid of GenericRepeatedField. +class LIBPROTOBUF_EXPORT GenericRepeatedField { + public: + inline GenericRepeatedField() {} + virtual ~GenericRepeatedField(); + + private: + // We only want GeneratedMessageReflection to see and use these, so we + // make them private. Yes, it is valid C++ for a subclass to implement + // a virtual method which is private in the superclass. Crazy, huh? + friend class GeneratedMessageReflection; + + virtual const void* GenericGet(int index) const = 0; + virtual void* GenericMutable(int index) = 0; + virtual void* GenericAdd() = 0; + virtual void GenericClear() = 0; + virtual int GenericSize() const = 0; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GenericRepeatedField); +}; + +} // namespace internal + +// RepeatedField is used to represent repeated fields of a primitive type (in +// other words, everything except strings and nested Messages). Most users will +// not ever use a RepeatedField directly; they will use the get-by-index, +// set-by-index, and add accessors that are generated for all repeated fields. +template +class RepeatedField : public internal::GenericRepeatedField { + public: + RepeatedField(); + ~RepeatedField(); + + int size() const; + + Element Get(int index) const; + Element* Mutable(int index); + void Set(int index, Element value); + void Add(Element value); + // Remove the last element in the array. + // We don't provide a way to remove any element other than the last + // because it invites inefficient use, such as O(n^2) filtering loops + // that should have been O(n). If you want to remove an element other + // than the last, the best way to do it is to re-arrange the elements + // so that the one you want removed is at the end, then call RemoveLast(). + void RemoveLast(); + void Clear(); + void MergeFrom(const RepeatedField& other); + + // Reserve space to expand the field to at least the given size. If the + // array is grown, it will always be at least doubled in size. + void Reserve(int new_size); + + // Gets the underlying array. This pointer is possibly invalidated by + // any add or remove operation. + Element* mutable_data(); + const Element* data() const; + + // Swap entire contents with "other". + void Swap(RepeatedField* other); + + // STL-like iterator support + typedef Element* iterator; + typedef const Element* const_iterator; + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + private: // See GenericRepeatedField for why this is private. + // implements GenericRepeatedField --------------------------------- + const void* GenericGet(int index) const; + void* GenericMutable(int index); + void* GenericAdd(); + void GenericClear(); + int GenericSize() const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedField); + + static const int kInitialSize = 4; + + Element* elements_; + int current_size_; + int total_size_; + + Element initial_space_[kInitialSize]; +}; + +namespace internal { +template class RepeatedPtrIterator; +} // namespace internal + +// RepeatedPtrField is like RepeatedField, but used for repeated strings or +// Messages. +template +class RepeatedPtrField : public internal::GenericRepeatedField { + public: + RepeatedPtrField(); + + // This constructor is only defined for RepeatedPtrField. + // When a RepeatedPtrField is created using this constructor, + // prototype->New() will be called to allocate new elements, rather than + // just using the "new" operator. This is useful for the implementation + // of DynamicMessage, but is not used by normal generated messages. + explicit RepeatedPtrField(const Message* prototype); + + ~RepeatedPtrField(); + + // Returns the prototype if one was passed to the constructor. + const Message* prototype() const; + + int size() const; + + const Element& Get(int index) const; + Element* Mutable(int index); + Element* Add(); + void RemoveLast(); // Remove the last element in the array. + void Clear(); + void MergeFrom(const RepeatedPtrField& other); + + // Reserve space to expand the field to at least the given size. This only + // resizes the pointer array; it doesn't allocate any objects. If the + // array is grown, it will always be at least doubled in size. + void Reserve(int new_size); + + // Gets the underlying array. This pointer is possibly invalidated by + // any add or remove operation. + Element** mutable_data(); + const Element* const* data() const; + + // Swap entire contents with "other". + void Swap(RepeatedPtrField* other); + + // STL-like iterator support + typedef internal::RepeatedPtrIterator iterator; + typedef internal::RepeatedPtrIterator const_iterator; + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + // Advanced memory management -------------------------------------- + // When hardcore memory management becomes necessary -- as it often + // does here at Google -- the following methods may be useful. + + // Add an already-allocated object, passing ownership to the + // RepeatedPtrField. + void AddAllocated(Element* value); + // Remove the last element and return it, passing ownership to the + // caller. + // Requires: size() > 0 + Element* ReleaseLast(); + + // When elements are removed by calls to RemoveLast() or Clear(), they + // are not actually freed. Instead, they are cleared and kept so that + // they can be reused later. This can save lots of CPU time when + // repeatedly reusing a protocol message for similar purposes. + // + // Really, extremely hardcore programs may actually want to manipulate + // these objects to better-optimize memory management. These methods + // allow that. + + // Get the number of cleared objects that are currently being kept + // around for reuse. + int ClearedCount(); + // Add an element to the pool of cleared objects, passing ownership to + // the RepeatedPtrField. The element must be cleared prior to calling + // this method. + void AddCleared(Element* value); + // Remove a single element from the cleared pool and return it, passing + // ownership to the caller. The element is guaranteed to be cleared. + // Requires: ClearedCount() > 0 + Element* ReleaseCleared(); + + private: // See GenericRepeatedField for why this is private. + // implements GenericRepeatedField --------------------------------- + const void* GenericGet(int index) const; + void* GenericMutable(int index); + void* GenericAdd(); + void GenericClear(); + int GenericSize() const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPtrField); + + static const int kInitialSize = 4; + + // prototype_ is used for RepeatedPtrField only (see constructor). + const Message* prototype_; + + Element** elements_; + int current_size_; + int allocated_size_; + int total_size_; + + Element* initial_space_[kInitialSize]; + + Element* NewElement(); +}; + +// implementation ==================================================== + +template +inline RepeatedField::RepeatedField() + : elements_(initial_space_), + current_size_(0), + total_size_(kInitialSize) { +} + +template +RepeatedField::~RepeatedField() { + if (elements_ != initial_space_) { + delete [] elements_; + } +} + +template +inline int RepeatedField::size() const { + return current_size_; +} + + +template +inline Element RepeatedField::Get(int index) const { + GOOGLE_DCHECK_LT(index, size()); + return elements_[index]; +} + +template +inline Element* RepeatedField::Mutable(int index) { + GOOGLE_DCHECK_LT(index, size()); + return elements_ + index; +} + +template +inline void RepeatedField::Set(int index, Element value) { + GOOGLE_DCHECK_LT(index, size()); + elements_[index] = value; +} + +template +inline void RepeatedField::Add(Element value) { + if (current_size_ == total_size_) Reserve(total_size_ + 1); + elements_[current_size_++] = value; +} + +template +inline void RepeatedField::RemoveLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + --current_size_; +} + +template +inline void RepeatedField::Clear() { + current_size_ = 0; +} + +template +void RepeatedField::MergeFrom(const RepeatedField& other) { + Reserve(current_size_ + other.current_size_); + memcpy(elements_ + current_size_, other.elements_, + sizeof(Element) * other.current_size_); + current_size_ += other.current_size_; +} + +template +inline Element* RepeatedField::mutable_data() { + return elements_; +} + +template +inline const Element* RepeatedField::data() const { + return elements_; +} + + +template +void RepeatedField::Swap(RepeatedField* other) { + Element* swap_elements = elements_; + int swap_current_size = current_size_; + int swap_total_size = total_size_; + // We may not be using initial_space_ but it's not worth checking. Just + // copy it anyway. + Element swap_initial_space[kInitialSize]; + memcpy(swap_initial_space, initial_space_, sizeof(initial_space_)); + + elements_ = other->elements_; + current_size_ = other->current_size_; + total_size_ = other->total_size_; + memcpy(initial_space_, other->initial_space_, sizeof(initial_space_)); + + other->elements_ = swap_elements; + other->current_size_ = swap_current_size; + other->total_size_ = swap_total_size; + memcpy(other->initial_space_, swap_initial_space, sizeof(swap_initial_space)); + + if (elements_ == other->initial_space_) { + elements_ = initial_space_; + } + if (other->elements_ == initial_space_) { + other->elements_ = other->initial_space_; + } +} + +template +inline typename RepeatedField::iterator +RepeatedField::begin() { + return elements_; +} +template +inline typename RepeatedField::const_iterator +RepeatedField::begin() const { + return elements_; +} +template +inline typename RepeatedField::iterator +RepeatedField::end() { + return elements_ + current_size_; +} +template +inline typename RepeatedField::const_iterator +RepeatedField::end() const { + return elements_ + current_size_; +} + + +template +const void* RepeatedField::GenericGet(int index) const { + GOOGLE_DCHECK_LT(index, size()); + return elements_ + index; +} + +template +void* RepeatedField::GenericMutable(int index) { + return Mutable(index); +} + +template +void* RepeatedField::GenericAdd() { + Add(Element()); + return Mutable(current_size_ - 1); +} + +template +void RepeatedField::GenericClear() { + Clear(); +} + +template +int RepeatedField::GenericSize() const { + return size(); +} + +template +inline void RepeatedField::Reserve(int new_size) { + if (total_size_ >= new_size) return; + + Element* old_elements = elements_; + total_size_ = max(total_size_ * 2, new_size); + elements_ = new Element[total_size_]; + memcpy(elements_, old_elements, current_size_ * sizeof(elements_[0])); + if (old_elements != initial_space_) { + delete [] old_elements; + } +} + +// ------------------------------------------------------------------- + +template +inline RepeatedPtrField::RepeatedPtrField() + : prototype_(NULL), + elements_(initial_space_), + current_size_(0), + allocated_size_(0), + total_size_(kInitialSize) { +} + +template <> +inline RepeatedPtrField::RepeatedPtrField(const Message* prototype) + : prototype_(prototype), + elements_(initial_space_), + current_size_(0), + allocated_size_(0), + total_size_(kInitialSize) { +} + +template +RepeatedPtrField::~RepeatedPtrField() { + for (int i = 0; i < allocated_size_; i++) { + delete elements_[i]; + } + if (elements_ != initial_space_) { + delete [] elements_; + } +} + +template <> +inline const Message* RepeatedPtrField::prototype() const { + return prototype_; +} + + +template +inline int RepeatedPtrField::size() const { + return current_size_; +} + + +template +inline const Element& RepeatedPtrField::Get(int index) const { + GOOGLE_DCHECK_LT(index, size()); + return *elements_[index]; +} + +template +inline Element* RepeatedPtrField::Mutable(int index) { + GOOGLE_DCHECK_LT(index, size()); + return elements_[index]; +} + +template +inline Element* RepeatedPtrField::Add() { + if (current_size_ < allocated_size_) return elements_[current_size_++]; + if (allocated_size_ == total_size_) Reserve(total_size_ + 1); + ++allocated_size_; + return elements_[current_size_++] = NewElement(); +} + +template +inline void RepeatedPtrField::RemoveLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + elements_[--current_size_]->Clear(); +} + +template <> +inline void RepeatedPtrField::RemoveLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + elements_[--current_size_]->clear(); +} + +template +void RepeatedPtrField::Clear() { + for (int i = 0; i < current_size_; i++) { + elements_[i]->Clear(); + } + current_size_ = 0; +} + +// Specialization defined in repeated_field.cc. +template <> +void LIBPROTOBUF_EXPORT RepeatedPtrField::Clear(); + +template +void RepeatedPtrField::MergeFrom(const RepeatedPtrField& other) { + Reserve(current_size_ + other.current_size_); + for (int i = 0; i < other.current_size_; i++) { + Add()->MergeFrom(other.Get(i)); + } +} + +template <> +inline void RepeatedPtrField::MergeFrom(const RepeatedPtrField& other) { + Reserve(current_size_ + other.current_size_); + for (int i = 0; i < other.current_size_; i++) { + Add()->assign(other.Get(i)); + } +} + + +template +inline Element** RepeatedPtrField::mutable_data() { + return elements_; +} + +template +inline const Element* const* RepeatedPtrField::data() const { + return elements_; +} + + +template +void RepeatedPtrField::Swap(RepeatedPtrField* other) { + Element** swap_elements = elements_; + int swap_current_size = current_size_; + int swap_allocated_size = allocated_size_; + int swap_total_size = total_size_; + // We may not be using initial_space_ but it's not worth checking. Just + // copy it anyway. + Element* swap_initial_space[kInitialSize]; + memcpy(swap_initial_space, initial_space_, sizeof(initial_space_)); + + elements_ = other->elements_; + current_size_ = other->current_size_; + allocated_size_ = other->allocated_size_; + total_size_ = other->total_size_; + memcpy(initial_space_, other->initial_space_, sizeof(initial_space_)); + + other->elements_ = swap_elements; + other->current_size_ = swap_current_size; + other->allocated_size_ = swap_allocated_size; + other->total_size_ = swap_total_size; + memcpy(other->initial_space_, swap_initial_space, sizeof(swap_initial_space)); + + if (elements_ == other->initial_space_) { + elements_ = initial_space_; + } + if (other->elements_ == initial_space_) { + other->elements_ = other->initial_space_; + } +} + + +template +inline void RepeatedPtrField::AddAllocated(Element* value) { + if (allocated_size_ == total_size_) Reserve(total_size_ + 1); + // We don't care about the order of cleared elements, so if there's one + // in the way, just move it to the back of the array. + if (current_size_ < allocated_size_) { + elements_[allocated_size_] = elements_[current_size_]; + } + ++allocated_size_; + elements_[current_size_++] = value; +} + +template +inline Element* RepeatedPtrField::ReleaseLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + Element* result = elements_[--current_size_]; + --allocated_size_; + if (current_size_ < allocated_size_) { + // There are cleared elements on the end; replace the removed element + // with the last allocated element. + elements_[current_size_] = elements_[allocated_size_]; + } + return result; +} + + +template +inline int RepeatedPtrField::ClearedCount() { + return allocated_size_ - current_size_; +} + +template +inline void RepeatedPtrField::AddCleared(Element* value) { + if (allocated_size_ == total_size_) Reserve(total_size_ + 1); + elements_[allocated_size_++] = value; +} + +template +inline Element* RepeatedPtrField::ReleaseCleared() { + GOOGLE_DCHECK_GT(allocated_size_, current_size_); + return elements_[--allocated_size_]; +} + + +template +const void* RepeatedPtrField::GenericGet(int index) const { + return &Get(index); +} + +template +void* RepeatedPtrField::GenericMutable(int index) { + return Mutable(index); +} + +template +void* RepeatedPtrField::GenericAdd() { + return Add(); +} + +template +void RepeatedPtrField::GenericClear() { + Clear(); +} + +template +int RepeatedPtrField::GenericSize() const { + return size(); +} + + +template +inline void RepeatedPtrField::Reserve(int new_size) { + if (total_size_ >= new_size) return; + + Element** old_elements = elements_; + total_size_ = max(total_size_ * 2, new_size); + elements_ = new Element*[total_size_]; + memcpy(elements_, old_elements, allocated_size_ * sizeof(elements_[0])); + if (old_elements != initial_space_) { + delete [] old_elements; + } +} + +template +inline Element* RepeatedPtrField::NewElement() { + return new Element; +} + +// RepeatedPtrField is alowed but requires a prototype since Message +// is abstract. +template <> +inline Message* RepeatedPtrField::NewElement() { + return prototype_->New(); +} + +// ------------------------------------------------------------------- + +namespace internal { + +// STL-like iterator implementation for RepeatedPtrField. You should not +// refer to this class directly; use RepeatedPtrField::iterator instead. +// +// The iterator for RepeatedPtrField, RepeatedPtrIterator, is +// very similar to iterator_ptr<> in util/gtl/iterator_adaptors-inl.h, +// but adds random-access operators and is slightly more specialized +// for using T** as its base type. I didn't re-use the other class to +// avoid an extra dependency. +// +// This code stolen from net/proto/proto-array-internal.h by Jeffrey Yasskin +// (jyasskin@google.com). +template +class RepeatedPtrIterator + : public std::iterator< + std::random_access_iterator_tag, + typename internal::remove_pointer< + typename internal::remove_pointer::type>::type> { + public: + typedef RepeatedPtrIterator iterator; + typedef typename iterator::reference reference; + typedef typename iterator::pointer pointer; + typedef typename iterator::difference_type difference_type; + + RepeatedPtrIterator() : it_(NULL) {} + explicit RepeatedPtrIterator(const It& it) : it_(it) {} + + // Allow "upcasting" from RepeatedPtrIterator to + // RepeatedPtrIterator. + template + RepeatedPtrIterator(const RepeatedPtrIterator& other) + : it_(other.base()) {} + + // Provide access to the wrapped iterator. + const It& base() const { return it_; } + + // dereferenceable + reference operator*() const { return **it_; } + pointer operator->() const { return &(operator*()); } + + // {inc,dec}rementable + iterator& operator++() { ++it_; return *this; } + iterator operator++(int) { return iterator(it_++); } + iterator& operator--() { --it_; return *this; } + iterator operator--(int) { return iterator(it_--); } + + // equality_comparable + bool operator==(const iterator& x) const { return it_ == x.it_; } + bool operator!=(const iterator& x) const { return it_ != x.it_; } + + // less_than_comparable + bool operator<(const iterator& x) const { return it_ < x.it_; } + bool operator<=(const iterator& x) const { return it_ <= x.it_; } + bool operator>(const iterator& x) const { return it_ > x.it_; } + bool operator>=(const iterator& x) const { return it_ >= x.it_; } + + // addable, subtractable + iterator& operator+=(difference_type d) { + it_ += d; + return *this; + } + friend iterator operator+(iterator it, difference_type d) { + it += d; + return it; + } + friend iterator operator+(difference_type d, iterator it) { + it += d; + return it; + } + iterator& operator-=(difference_type d) { + it_ -= d; + return *this; + } + friend iterator operator-(iterator it, difference_type d) { + it -= d; + return it; + } + + // indexable + reference operator[](difference_type d) const { return *(*this + d); } + + // random access iterator + difference_type operator-(const iterator& x) const { return it_ - x.it_; } + + private: + // The internal iterator. + It it_; +}; + +} // namespace internal + +template +inline typename RepeatedPtrField::iterator +RepeatedPtrField::begin() { + return iterator(elements_); +} +template +inline typename RepeatedPtrField::const_iterator +RepeatedPtrField::begin() const { + return iterator(elements_); +} +template +inline typename RepeatedPtrField::iterator +RepeatedPtrField::end() { + return iterator(elements_ + current_size_); +} +template +inline typename RepeatedPtrField::const_iterator +RepeatedPtrField::end() const { + return iterator(elements_ + current_size_); +} + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_REPEATED_FIELD_H__ diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc new file mode 100644 index 00000000..eb9b096f --- /dev/null +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -0,0 +1,603 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// TODO(kenton): Improve this unittest to bring it up to the standards of +// other proto2 unittests. + +#include + +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +// Test operations on a RepeatedField which is small enough that it does +// not allocate a separate array for storage. +TEST(RepeatedField, Small) { + RepeatedField field; + + EXPECT_EQ(field.size(), 0); + + field.Add(5); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), 5); + + field.Add(42); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), 5); + EXPECT_EQ(field.Get(1), 42); + + field.Set(1, 23); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), 5); + EXPECT_EQ(field.Get(1), 23); + + field.RemoveLast(); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), 5); + + field.Clear(); + + EXPECT_EQ(field.size(), 0); +} + +// Test operations on a RepeatedField which is large enough to allocate a +// separate array. +TEST(RepeatedField, Large) { + RepeatedField field; + + for (int i = 0; i < 16; i++) { + field.Add(i * i); + } + + EXPECT_EQ(field.size(), 16); + + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field.Get(i), i * i); + } +} + +// Test swapping between various types of RepeatedFields. +TEST(RepeatedField, SwapSmallSmall) { + RepeatedField field1; + RepeatedField field2; + + field1.Add(5); + field1.Add(42); + + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 0); + EXPECT_EQ(field2.size(), 2); + EXPECT_EQ(field2.Get(0), 5); + EXPECT_EQ(field2.Get(1), 42); +} + +TEST(RepeatedField, SwapLargeSmall) { + RepeatedField field1; + RepeatedField field2; + + for (int i = 0; i < 16; i++) { + field1.Add(i * i); + } + field2.Add(5); + field2.Add(42); + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 2); + EXPECT_EQ(field1.Get(0), 5); + EXPECT_EQ(field1.Get(1), 42); + EXPECT_EQ(field2.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field2.Get(i), i * i); + } +} + +TEST(RepeatedField, SwapLargeLarge) { + RepeatedField field1; + RepeatedField field2; + + field1.Add(5); + field1.Add(42); + for (int i = 0; i < 16; i++) { + field1.Add(i); + field2.Add(i * i); + } + field2.Swap(&field1); + + EXPECT_EQ(field1.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field1.Get(i), i * i); + } + EXPECT_EQ(field2.size(), 18); + EXPECT_EQ(field2.Get(0), 5); + EXPECT_EQ(field2.Get(1), 42); + for (int i = 2; i < 18; i++) { + EXPECT_EQ(field2.Get(i), i - 2); + } +} + +// Determines how much space was reserved by the given field by adding elements +// to it until it re-allocates its space. +static int ReservedSpace(RepeatedField* field) { + const int* ptr = field->data(); + do { + field->Add(0); + } while (field->data() == ptr); + + return field->size() - 1; +} + +TEST(RepeatedField, ReserveMoreThanDouble) { + // Reserve more than double the previous space in the field and expect the + // field to reserve exactly the amount specified. + RepeatedField field; + field.Reserve(20); + + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedField, ReserveLessThanDouble) { + // Reserve less than double the previous space in the field and expect the + // field to grow by double instead. + RepeatedField field; + field.Reserve(20); + field.Reserve(30); + + EXPECT_EQ(40, ReservedSpace(&field)); +} + +TEST(RepeatedField, ReserveLessThanExisting) { + // Reserve less than the previous space in the field and expect the + // field to not re-allocate at all. + RepeatedField field; + field.Reserve(20); + const int* previous_ptr = field.data(); + field.Reserve(10); + + EXPECT_EQ(previous_ptr, field.data()); + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedField, MergeFrom) { + RepeatedField source, destination; + + source.Add(4); + source.Add(5); + + destination.Add(1); + destination.Add(2); + destination.Add(3); + + destination.MergeFrom(source); + + ASSERT_EQ(5, destination.size()); + + EXPECT_EQ(1, destination.Get(0)); + EXPECT_EQ(2, destination.Get(1)); + EXPECT_EQ(3, destination.Get(2)); + EXPECT_EQ(4, destination.Get(3)); + EXPECT_EQ(5, destination.Get(4)); +} + +TEST(RepeatedField, MutableDataIsMutable) { + RepeatedField field; + field.Add(1); + EXPECT_EQ(1, field.Get(0)); + // The fact that this line compiles would be enough, but we'll check the + // value anyway. + *field.mutable_data() = 2; + EXPECT_EQ(2, field.Get(0)); +} + +// =================================================================== +// RepeatedPtrField tests. These pretty much just mirror the RepeatedField +// tests above. + +TEST(RepeatedPtrField, Small) { + RepeatedPtrField field; + + EXPECT_EQ(field.size(), 0); + + field.Add()->assign("foo"); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), "foo"); + + field.Add()->assign("bar"); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), "foo"); + EXPECT_EQ(field.Get(1), "bar"); + + field.Mutable(1)->assign("baz"); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), "foo"); + EXPECT_EQ(field.Get(1), "baz"); + + field.RemoveLast(); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), "foo"); + + field.Clear(); + + EXPECT_EQ(field.size(), 0); +} + +TEST(RepeatedPtrField, Large) { + RepeatedPtrField field; + + for (int i = 0; i < 16; i++) { + *field.Add() += 'a' + i; + } + + EXPECT_EQ(field.size(), 16); + + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field.Get(i).size(), 1); + EXPECT_EQ(field.Get(i)[0], 'a' + i); + } +} + +TEST(RepeatedPtrField, SwapSmallSmall) { + RepeatedPtrField field1; + RepeatedPtrField field2; + + field1.Add()->assign("foo"); + field1.Add()->assign("bar"); + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 0); + EXPECT_EQ(field2.size(), 2); + EXPECT_EQ(field2.Get(0), "foo"); + EXPECT_EQ(field2.Get(1), "bar"); +} + +TEST(RepeatedPtrField, SwapLargeSmall) { + RepeatedPtrField field1; + RepeatedPtrField field2; + + field2.Add()->assign("foo"); + field2.Add()->assign("bar"); + for (int i = 0; i < 16; i++) { + *field1.Add() += 'a' + i; + } + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 2); + EXPECT_EQ(field1.Get(0), "foo"); + EXPECT_EQ(field1.Get(1), "bar"); + EXPECT_EQ(field2.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field2.Get(i).size(), 1); + EXPECT_EQ(field2.Get(i)[0], 'a' + i); + } +} + +TEST(RepeatedPtrField, SwapLargeLarge) { + RepeatedPtrField field1; + RepeatedPtrField field2; + + field1.Add()->assign("foo"); + field1.Add()->assign("bar"); + for (int i = 0; i < 16; i++) { + *field1.Add() += 'A' + i; + *field2.Add() += 'a' + i; + } + field2.Swap(&field1); + + EXPECT_EQ(field1.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field1.Get(i).size(), 1); + EXPECT_EQ(field1.Get(i)[0], 'a' + i); + } + EXPECT_EQ(field2.size(), 18); + EXPECT_EQ(field2.Get(0), "foo"); + EXPECT_EQ(field2.Get(1), "bar"); + for (int i = 2; i < 18; i++) { + EXPECT_EQ(field2.Get(i).size(), 1); + EXPECT_EQ(field2.Get(i)[0], 'A' + i - 2); + } +} + +static int ReservedSpace(RepeatedPtrField* field) { + const string* const* ptr = field->data(); + do { + field->Add(); + } while (field->data() == ptr); + + return field->size() - 1; +} + +TEST(RepeatedPtrField, ReserveMoreThanDouble) { + RepeatedPtrField field; + field.Reserve(20); + + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedPtrField, ReserveLessThanDouble) { + RepeatedPtrField field; + field.Reserve(20); + field.Reserve(30); + + EXPECT_EQ(40, ReservedSpace(&field)); +} + +TEST(RepeatedPtrField, ReserveLessThanExisting) { + RepeatedPtrField field; + field.Reserve(20); + const string* const* previous_ptr = field.data(); + field.Reserve(10); + + EXPECT_EQ(previous_ptr, field.data()); + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedPtrField, ReserveDoesntLoseAllocated) { + // Check that a bug is fixed: An earlier implementation of Reserve() + // failed to copy pointers to allocated-but-cleared objects, possibly + // leading to segfaults. + RepeatedPtrField field; + string* first = field.Add(); + field.RemoveLast(); + + field.Reserve(20); + EXPECT_EQ(first, field.Add()); +} + +// Clearing elements is tricky with RepeatedPtrFields since the memory for +// the elements is retained and reused. +TEST(RepeatedPtrField, ClearedElements) { + RepeatedPtrField field; + + string* original = field.Add(); + *original = "foo"; + + EXPECT_EQ(field.ClearedCount(), 0); + + field.RemoveLast(); + EXPECT_TRUE(original->empty()); + EXPECT_EQ(field.ClearedCount(), 1); + + EXPECT_EQ(field.Add(), original); // Should return same string for reuse. + + EXPECT_EQ(field.ReleaseLast(), original); // We take ownership. + EXPECT_EQ(field.ClearedCount(), 0); + + EXPECT_NE(field.Add(), original); // Should NOT return the same string. + EXPECT_EQ(field.ClearedCount(), 0); + + field.AddAllocated(original); // Give ownership back. + EXPECT_EQ(field.ClearedCount(), 0); + EXPECT_EQ(field.Mutable(1), original); + + field.Clear(); + EXPECT_EQ(field.ClearedCount(), 2); + EXPECT_EQ(field.ReleaseCleared(), original); // Take ownership again. + EXPECT_EQ(field.ClearedCount(), 1); + EXPECT_NE(field.Add(), original); + EXPECT_EQ(field.ClearedCount(), 0); + EXPECT_NE(field.Add(), original); + EXPECT_EQ(field.ClearedCount(), 0); + + field.AddCleared(original); // Give ownership back, but as a cleared object. + EXPECT_EQ(field.ClearedCount(), 1); + EXPECT_EQ(field.Add(), original); + EXPECT_EQ(field.ClearedCount(), 0); +} + +TEST(RepeatedPtrField, MergeFrom) { + RepeatedPtrField source, destination; + + source.Add()->assign("4"); + source.Add()->assign("5"); + + destination.Add()->assign("1"); + destination.Add()->assign("2"); + destination.Add()->assign("3"); + + destination.MergeFrom(source); + + ASSERT_EQ(5, destination.size()); + + EXPECT_EQ("1", destination.Get(0)); + EXPECT_EQ("2", destination.Get(1)); + EXPECT_EQ("3", destination.Get(2)); + EXPECT_EQ("4", destination.Get(3)); + EXPECT_EQ("5", destination.Get(4)); +} + +TEST(RepeatedPtrField, MutableDataIsMutable) { + RepeatedPtrField field; + *field.Add() = "1"; + EXPECT_EQ("1", field.Get(0)); + // The fact that this line compiles would be enough, but we'll check the + // value anyway. + string** data = field.mutable_data(); + **data = "2"; + EXPECT_EQ("2", field.Get(0)); +} + +// =================================================================== + +// Iterator tests stolen from net/proto/proto-array_unittest. +class RepeatedFieldIteratorTest : public testing::Test { + protected: + virtual void SetUp() { + for (int i = 0; i < 3; ++i) { + proto_array_.Add(i); + } + } + + RepeatedField proto_array_; +}; + +TEST_F(RepeatedFieldIteratorTest, Convertible) { + RepeatedField::iterator iter = proto_array_.begin(); + RepeatedField::const_iterator c_iter = iter; + EXPECT_EQ(0, *c_iter); +} + +TEST_F(RepeatedFieldIteratorTest, MutableIteration) { + RepeatedField::iterator iter = proto_array_.begin(); + EXPECT_EQ(0, *iter); + ++iter; + EXPECT_EQ(1, *iter++); + EXPECT_EQ(2, *iter); + ++iter; + EXPECT_TRUE(proto_array_.end() == iter); + + EXPECT_EQ(2, *(proto_array_.end() - 1)); +} + +TEST_F(RepeatedFieldIteratorTest, ConstIteration) { + const RepeatedField& const_proto_array = proto_array_; + RepeatedField::const_iterator iter = const_proto_array.begin(); + EXPECT_EQ(0, *iter); + ++iter; + EXPECT_EQ(1, *iter++); + EXPECT_EQ(2, *iter); + ++iter; + EXPECT_TRUE(proto_array_.end() == iter); + EXPECT_EQ(2, *(proto_array_.end() - 1)); +} + +TEST_F(RepeatedFieldIteratorTest, Mutation) { + RepeatedField::iterator iter = proto_array_.begin(); + *iter = 7; + EXPECT_EQ(7, proto_array_.Get(0)); +} + +// ------------------------------------------------------------------- + +class RepeatedPtrFieldIteratorTest : public testing::Test { + protected: + virtual void SetUp() { + proto_array_.Add()->assign("foo"); + proto_array_.Add()->assign("bar"); + proto_array_.Add()->assign("baz"); + } + + RepeatedPtrField proto_array_; +}; + +TEST_F(RepeatedPtrFieldIteratorTest, Convertible) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + RepeatedPtrField::const_iterator c_iter = iter; +} + +TEST_F(RepeatedPtrFieldIteratorTest, MutableIteration) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + EXPECT_EQ("foo", *iter); + ++iter; + EXPECT_EQ("bar", *(iter++)); + EXPECT_EQ("baz", *iter); + ++iter; + EXPECT_TRUE(proto_array_.end() == iter); + EXPECT_EQ("baz", *(--proto_array_.end())); +} + +TEST_F(RepeatedPtrFieldIteratorTest, ConstIteration) { + const RepeatedPtrField& const_proto_array = proto_array_; + RepeatedPtrField::const_iterator iter = const_proto_array.begin(); + EXPECT_EQ("foo", *iter); + ++iter; + EXPECT_EQ("bar", *(iter++)); + EXPECT_EQ("baz", *iter); + ++iter; + EXPECT_TRUE(const_proto_array.end() == iter); + EXPECT_EQ("baz", *(--const_proto_array.end())); +} + +TEST_F(RepeatedPtrFieldIteratorTest, RandomAccess) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + RepeatedPtrField::iterator iter2 = iter; + ++iter2; + ++iter2; + EXPECT_TRUE(iter + 2 == iter2); + EXPECT_TRUE(iter == iter2 - 2); + EXPECT_EQ("baz", iter[2]); + EXPECT_EQ("baz", *(iter + 2)); + EXPECT_EQ(3, proto_array_.end() - proto_array_.begin()); +} + +TEST_F(RepeatedPtrFieldIteratorTest, Comparable) { + RepeatedPtrField::const_iterator iter = proto_array_.begin(); + RepeatedPtrField::const_iterator iter2 = iter + 1; + EXPECT_TRUE(iter == iter); + EXPECT_TRUE(iter != iter2); + EXPECT_TRUE(iter < iter2); + EXPECT_TRUE(iter <= iter2); + EXPECT_TRUE(iter <= iter); + EXPECT_TRUE(iter2 > iter); + EXPECT_TRUE(iter2 >= iter); + EXPECT_TRUE(iter >= iter); +} + +// Uninitialized iterator does not point to any of the RepeatedPtrField. +// Dereferencing an uninitialized iterator crashes the process. +TEST_F(RepeatedPtrFieldIteratorTest, UninitializedIterator) { + RepeatedPtrField::iterator iter; + EXPECT_TRUE(iter != proto_array_.begin()); + EXPECT_TRUE(iter != proto_array_.begin() + 1); + EXPECT_TRUE(iter != proto_array_.begin() + 2); + EXPECT_TRUE(iter != proto_array_.begin() + 3); + EXPECT_TRUE(iter != proto_array_.end()); +#ifdef GTEST_HAS_DEATH_TEST + ASSERT_DEATH(GOOGLE_LOG(INFO) << *iter, ""); +#endif +} + +TEST_F(RepeatedPtrFieldIteratorTest, STLAlgorithms_lower_bound) { + proto_array_.Clear(); + proto_array_.Add()->assign("a"); + proto_array_.Add()->assign("c"); + proto_array_.Add()->assign("d"); + proto_array_.Add()->assign("n"); + proto_array_.Add()->assign("p"); + proto_array_.Add()->assign("x"); + proto_array_.Add()->assign("y"); + + string v = "f"; + RepeatedPtrField::const_iterator it = + lower_bound(proto_array_.begin(), proto_array_.end(), v); + EXPECT_EQ(*it, "n"); + EXPECT_TRUE(it == proto_array_.begin() + 3); +} + +TEST_F(RepeatedPtrFieldIteratorTest, Mutation) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + *iter = "qux"; + EXPECT_EQ("qux", proto_array_.Get(0)); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/service.cc b/src/google/protobuf/service.cc new file mode 100644 index 00000000..0c997930 --- /dev/null +++ b/src/google/protobuf/service.cc @@ -0,0 +1,32 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +namespace google { +namespace protobuf { + +Service::~Service() {} +RpcChannel::~RpcChannel() {} +RpcController::~RpcController() {} + +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/service.h b/src/google/protobuf/service.h new file mode 100644 index 00000000..f3e78e7b --- /dev/null +++ b/src/google/protobuf/service.h @@ -0,0 +1,273 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This module declares the abstract interfaces underlying proto2 RPC +// services. These are intented to be independent of any particular RPC +// implementation, so that proto2 services can be used on top of a variety +// of implementations. +// +// +// When you use the protocol compiler to compile a service definition, it +// generates two classes: An abstract interface for the service (with +// methods matching the service definition) and a "stub" implementation. +// A stub is just a type-safe wrapper around an RpcChannel which emulates a +// local implementation of the service. +// +// For example, the service definition: +// service MyService { +// rpc Foo(MyRequest) returns(MyResponse); +// } +// will generate abstract interface "MyService" and class "MyService::Stub". +// You could implement a MyService as follows: +// class MyServiceImpl : public MyService { +// public: +// MyServiceImpl() {} +// ~MyServiceImpl() {} +// +// // implements MyService --------------------------------------- +// +// void Foo(google::protobuf::RpcController* controller, +// const MyRequest* request, +// MyResponse* response, +// Closure* done) { +// // ... read request and fill in response ... +// done->Run(); +// } +// }; +// You would then register an instance of MyServiceImpl with your RPC server +// implementation. (How to do that depends on the implementation.) +// +// To call a remote MyServiceImpl, first you need an RpcChannel connected to it. +// How to construct a channel depends, again, on your RPC implementation. +// Here we use a hypothentical "MyRpcChannel" as an example: +// MyRpcChannel channel("rpc:hostname:1234/myservice"); +// MyRpcController controller; +// MyServiceImpl::Stub stub(&channel); +// FooRequest request; +// FooRespnose response; +// +// // ... fill in request ... +// +// stub.Foo(&controller, request, &response, NewCallback(HandleResponse)); +// +// On Thread-Safety: +// +// Different RPC implementations may make different guarantees about what +// threads they may run callbacks on, and what threads the application is +// allowed to use to call the RPC system. Portable software should be ready +// for callbacks to be called on any thread, but should not try to call the +// RPC system from any thread except for the ones on which it received the +// callbacks. Realistically, though, simple software will probably want to +// use a single-threaded RPC system while high-end software will want to +// use multiple threads. RPC implementations should provide multiple +// choices. + +#ifndef GOOGLE_PROTOBUF_SERVICE_H__ +#define GOOGLE_PROTOBUF_SERVICE_H__ + +#include +#include + +namespace google { +namespace protobuf { + +// Defined in this file. +class Service; +class RpcController; +class RpcChannel; + +// Defined in other files. +class Descriptor; // descriptor.h +class ServiceDescriptor; // descriptor.h +class MethodDescriptor; // descriptor.h +class Message; // message.h + +// Abstract base interface for protocol-buffer-based RPC services. Services +// themselves are abstract interfaces (implemented either by servers or as +// stubs), but they subclass this base interface. The methods of this +// interface can be used to call the methods of the Service without knowing +// its exact type at compile time (analogous to Message::Reflection). +class LIBPROTOBUF_EXPORT Service { + public: + inline Service() {} + virtual ~Service(); + + // When constructing a stub, you may pass STUB_OWNS_CHANNEL as the second + // parameter to the constructor to tell it to delete its RpcChannel when + // destroyed. + enum ChannelOwnership { + STUB_OWNS_CHANNEL, + STUB_DOESNT_OWN_CHANNEL + }; + + // Get the ServiceDescriptor describing this service and its methods. + virtual const ServiceDescriptor* GetDescriptor() = 0; + + // Call a method of the service specified by MethodDescriptor. This is + // normally implemented as a simple switch() that calls the standard + // definitions of the service's methods. + // + // Preconditions: + // * method->service() == GetDescriptor() + // * request and response are of the exact same classes as the objects + // returned by GetRequestPrototype(method) and + // GetResponsePrototype(method). + // * After the call has started, the request must not be modified and the + // response must not be accessed at all until "done" is called. + // * "controller" is of the correct type for the RPC implementation being + // used by this Service. For stubs, the "correct type" depends on the + // RpcChannel which the stub is using. Server-side Service + // implementations are expected to accept whatever type of RpcController + // the server-side RPC implementation uses. + // + // Postconditions: + // * "done" will be called when the method is complete. This may be + // before CallMethod() returns or it may be at some point in the future. + // * If the RPC succeeded, "response" contains the response returned by + // the server. + // * If the RPC failed, "response"'s contents are undefined. The + // RpcController can be queried to determine if an error occurred and + // possibly to get more information about the error. + virtual void CallMethod(const MethodDescriptor* method, + RpcController* controller, + const Message* request, + Message* response, + Closure* done) = 0; + + // CallMethod() requires that the request and response passed in are of a + // particular subclass of Message. GetRequestPrototype() and + // GetResponsePrototype() get the default instances of these required types. + // You can then call Message::New() on these instances to construct mutable + // objects which you can then pass to CallMethod(). + // + // Example: + // const MethodDescriptor* method = + // service->GetDescriptor()->FindMethodByName("Foo"); + // Message* request = stub->GetRequestPrototype (method)->New(); + // Message* response = stub->GetResponsePrototype(method)->New(); + // request->ParseFromString(input); + // service->CallMethod(method, *request, response, callback); + virtual const Message& GetRequestPrototype( + const MethodDescriptor* method) const = 0; + virtual const Message& GetResponsePrototype( + const MethodDescriptor* method) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Service); +}; + +// An RpcController mediates a single method call. The primary purpose of +// the controller is to provide a way to manipulate settings specific to the +// RPC implementation and to find out about RPC-level errors. +// +// The methods provided by the RpcController interface are intended to be a +// "least common denominator" set of features which we expect all +// implementations to support. Specific implementations may provide more +// advanced features (e.g. deadline propagation). +class LIBPROTOBUF_EXPORT RpcController { + public: + inline RpcController() {} + virtual ~RpcController(); + + // Client-side methods --------------------------------------------- + // These calls may be made from the client side only. Their results + // are undefined on the server side (may crash). + + // Resets the RpcController to its initial state so that it may be reused in + // a new call. Must not be called while an RPC is in progress. + virtual void Reset() = 0; + + // After a call has finished, returns true if the call failed. The possible + // reasons for failure depend on the RPC implementation. Failed() must not + // be called before a call has finished. If Failed() returns true, the + // contents of the response message are undefined. + virtual bool Failed() const = 0; + + // If Failed() is true, returns a human-readable description of the error. + virtual string ErrorText() const = 0; + + // Advises the RPC system that the caller desires that the RPC call be + // canceled. The RPC system may cancel it immediately, may wait awhile and + // then cancel it, or may not even cancel the call at all. If the call is + // canceled, the "done" callback will still be called and the RpcController + // will indicate that the call failed at that time. + virtual void StartCancel() = 0; + + // Server-side methods --------------------------------------------- + // These calls may be made from the server side only. Their results + // are undefined on the client side (may crash). + + // Causes Failed() to return true on the client side. "reason" will be + // incorporated into the message returned by ErrorText(). If you find + // you need to return machine-readable information about failures, you + // should incorporate it into your response protocol buffer and should + // NOT call SetFailed(). + virtual void SetFailed(const string& reason) = 0; + + // If true, indicates that the client canceled the RPC, so the server may + // as well give up on replying to it. The server should still call the + // final "done" callback. + virtual bool IsCanceled() const = 0; + + // Asks that the given callback be called when the RPC is canceled. The + // callback will always be called exactly once. If the RPC completes without + // being canceled, the callback will be called after completion. If the RPC + // has already been canceled when NotifyOnCancel() is called, the callback + // will be called immediately. + // + // NotifyOnCancel() must be called no more than once per request. + virtual void NotifyOnCancel(Closure* callback) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcController); +}; + +// Abstract interface for an RPC channel. An RpcChannel represents a +// communication line to a Service which can be used to call that Service's +// methods. The Service may be running on another machine. Normally, you +// should not call an RpcChannel directly, but instead construct a stub Service +// wrapping it. Example: +// RpcChannel* channel = new MyRpcChannel("remotehost.example.com:1234"); +// MyService* service = new MyService::Stub(channel); +// service->MyMethod(request, &response, callback); +class LIBPROTOBUF_EXPORT RpcChannel { + public: + inline RpcChannel() {} + virtual ~RpcChannel(); + + // Call the given method of the remote service. The signature of this + // procedure looks the same as Service::CallMethod(), but the requirements + // are less strict in one important way: the request and response objects + // need not be of any specific class as long as their descriptors are + // method->input_type() and method->output_type(). + virtual void CallMethod(const MethodDescriptor* method, + RpcController* controller, + const Message* request, + Message* response, + Closure* done) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_SERVICE_H__ diff --git a/src/google/protobuf/stubs/common.cc b/src/google/protobuf/stubs/common.cc new file mode 100644 index 00000000..d7182841 --- /dev/null +++ b/src/google/protobuf/stubs/common.cc @@ -0,0 +1,261 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN // We only need minimal includes +#include +#elif defined(HAVE_PTHREAD) +#include +#else +#error "No suitable threading library available." +#endif + +namespace google { +namespace protobuf { + +namespace internal { + +void VerifyVersion(int headerVersion, + int minLibraryVersion, + const char* filename) { + if (GOOGLE_PROTOBUF_VERSION < minLibraryVersion) { + // Library is too old for headers. + GOOGLE_LOG(FATAL) + << "This program requires version " << VersionString(minLibraryVersion) + << " of the Protocol Buffer runtime library, but the installed version " + "is " << VersionString(GOOGLE_PROTOBUF_VERSION) << ". Please update " + "your library. If you compiled the program yourself, make sure that " + "your headers are from the same version of Protocol Buffers as your " + "link-time library. (Version verification failed in \"" + << filename << "\".)"; + } + if (headerVersion < kMinHeaderVersionForLibrary) { + // Headers are too old for library. + GOOGLE_LOG(FATAL) + << "This program was compiled against version " + << VersionString(headerVersion) << " of the Protocol Buffer runtime " + "library, which is not compatible with the installed version (" + << VersionString(GOOGLE_PROTOBUF_VERSION) << "). Contact the program " + "author for an update. If you compiled the program yourself, make " + "sure that your headers are from the same version of Protocol Buffers " + "as your link-time library. (Version verification failed in \"" + << filename << "\".)"; + } +} + +string VersionString(int version) { + int major = version / 1000000; + int minor = (version / 1000) % 1000; + int micro = version % 1000; + + return strings::Substitute("$0.$1.$2", major, minor, micro); +} + +} // namespace internal + +// =================================================================== +// emulates google3/base/logging.cc + +namespace internal { + +void DefaultLogHandler(LogLevel level, const char* filename, int line, + const string& message) { + static const char* level_names[] = { "INFO", "WARNING", "ERROR", "FATAL" }; + + // We use fprintf() instead of cerr because we want this to work at static + // initialization time. + fprintf(stderr, "libprotobuf %s %s:%d] %s\n", + level_names[level], filename, line, message.c_str()); + fflush(stderr); // Needed on MSVC. +} + +void NullLogHandler(LogLevel level, const char* filename, int line, + const string& message) { + // Nothing. +} + +static LogHandler* log_handler_ = &DefaultLogHandler; +static int log_silencer_count_ = 0; +static Mutex log_silencer_count_mutex_; + +static string SimpleCtoa(char c) { return string(1, c); } + +#undef DECLARE_STREAM_OPERATOR +#define DECLARE_STREAM_OPERATOR(TYPE, TOSTRING) \ + LogMessage& LogMessage::operator<<(TYPE value) { \ + message_ += TOSTRING(value); \ + return *this; \ + } + +DECLARE_STREAM_OPERATOR(const string&, ) +DECLARE_STREAM_OPERATOR(const char* , ) +DECLARE_STREAM_OPERATOR(char , SimpleCtoa) +DECLARE_STREAM_OPERATOR(int , SimpleItoa) +DECLARE_STREAM_OPERATOR(uint , SimpleItoa) +DECLARE_STREAM_OPERATOR(double , SimpleDtoa) +#undef DECLARE_STREAM_OPERATOR + +LogMessage::LogMessage(LogLevel level, const char* filename, int line) + : level_(level), filename_(filename), line_(line) {} +LogMessage::~LogMessage() {} + +void LogMessage::Finish() { + bool suppress = false; + + if (level_ != LOGLEVEL_FATAL) { + MutexLock lock(&internal::log_silencer_count_mutex_); + suppress = internal::log_silencer_count_ > 0; + } + + if (!suppress) { + internal::log_handler_(level_, filename_, line_, message_); + } + + if (level_ == LOGLEVEL_FATAL) { + abort(); + } +} + +void LogFinisher::operator=(LogMessage& other) { + other.Finish(); +} + +} // namespace internal + +LogHandler* SetLogHandler(LogHandler* new_func) { + LogHandler* old = internal::log_handler_; + if (old == &internal::NullLogHandler) { + old = NULL; + } + if (new_func == NULL) { + internal::log_handler_ = &internal::NullLogHandler; + } else { + internal::log_handler_ = new_func; + } + return old; +} + +LogSilencer::LogSilencer() { + MutexLock lock(&internal::log_silencer_count_mutex_); + ++internal::log_silencer_count_; +}; + +LogSilencer::~LogSilencer() { + MutexLock lock(&internal::log_silencer_count_mutex_); + --internal::log_silencer_count_; +}; + +// =================================================================== +// emulates google3/base/callback.cc + +Closure::~Closure() {} + +namespace internal { FunctionClosure0::~FunctionClosure0() {} } + +void DoNothing() {} + +// =================================================================== +// emulates google3/base/mutex.cc + +#ifdef _WIN32 + +struct Mutex::Internal { + CRITICAL_SECTION mutex; +#ifndef NDEBUG + // Used only to implement AssertHeld(). + DWORD thread_id; +#endif +}; + +Mutex::Mutex() + : mInternal(new Internal) { + InitializeCriticalSection(&mInternal->mutex); +} + +Mutex::~Mutex() { + DeleteCriticalSection(&mInternal->mutex); + delete mInternal; +} + +void Mutex::Lock() { + EnterCriticalSection(&mInternal->mutex); +#ifndef NDEBUG + mInternal->thread_id = GetCurrentThreadId(); +#endif +} + +void Mutex::Unlock() { +#ifndef NDEBUG + mInternal->thread_id = 0; +#endif + LeaveCriticalSection(&mInternal->mutex); +} + +void Mutex::AssertHeld() { +#ifndef NDEBUG + GOOGLE_DCHECK_EQ(mInternal->thread_id, GetCurrentThreadId()); +#endif +} + +#elif defined(HAVE_PTHREAD) + +struct Mutex::Internal { + pthread_mutex_t mutex; +}; + +Mutex::Mutex() + : mInternal(new Internal) { + pthread_mutex_init(&mInternal->mutex, NULL); +} + +Mutex::~Mutex() { + pthread_mutex_destroy(&mInternal->mutex); + delete mInternal; +} + +void Mutex::Lock() { + int result = pthread_mutex_lock(&mInternal->mutex); + if (result != 0) { + GOOGLE_LOG(FATAL) << "pthread_mutex_lock: " << strerror(result); + } +} + +void Mutex::Unlock() { + int result = pthread_mutex_unlock(&mInternal->mutex); + if (result != 0) { + GOOGLE_LOG(FATAL) << "pthread_mutex_unlock: " << strerror(result); + } +} + +void Mutex::AssertHeld() { + // pthreads dosn't provide a way to check which thread holds the mutex. + // TODO(kenton): Maybe keep track of locking thread ID like with WIN32? +} + +#endif + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h new file mode 100644 index 00000000..03b176a3 --- /dev/null +++ b/src/google/protobuf/stubs/common.h @@ -0,0 +1,1061 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) and others +// +// Contains basic types and utilities used by the rest of the library. + +#ifndef GOOGLE_PROTOBUF_COMMON_H__ +#define GOOGLE_PROTOBUF_COMMON_H__ + +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif + +namespace std {} + +namespace google { +namespace protobuf { + +using namespace std; // Don't do this at home, kids. + +#undef GOOGLE_DISALLOW_EVIL_CONSTRUCTORS +#define GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#ifdef _MSC_VER + #ifdef LIBPROTOBUF_EXPORTS + #define LIBPROTOBUF_EXPORT __declspec(dllexport) + #else + #define LIBPROTOBUF_EXPORT __declspec(dllimport) + #endif + #ifdef LIBPROTOC_EXPORTS + #define LIBPROTOC_EXPORT __declspec(dllexport) + #else + #define LIBPROTOC_EXPORT __declspec(dllimport) + #endif +#else + #define LIBPROTOBUF_EXPORT + #define LIBPROTOC_EXPORT +#endif + +namespace internal { + +// Some of these constants are macros rather than const ints so that they can +// be used in #if directives. + +// The current version, represented as a single integer to make comparison +// easier: major * 10^6 + minor * 10^3 + micro +#define GOOGLE_PROTOBUF_VERSION 2000001 + +// The minimum library version which works with the current version of the +// headers. +#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION 2000000 + +// The minimum header version which works with the current version of +// the library. This constant should only be used by protoc's C++ code +// generator. +static const int kMinHeaderVersionForLibrary = 2000000; + +// The minimum protoc version which works with the current version of the +// headers. +#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 2000000 + +// The minimum header version which works with the current version of +// protoc. This constant should only be used in VerifyVersion(). +static const int kMinHeaderVersionForProtoc = 2000000; + +// Verifies that the headers and libraries are compatible. Use the macro +// below to call this. +void LIBPROTOBUF_EXPORT VerifyVersion(int headerVersion, int minLibraryVersion, + const char* filename); + +// Converts a numeric version number to a string. +string LIBPROTOBUF_EXPORT VersionString(int version); + +} // namespace internal + +// Place this macro in your main() function (or somewhere before you attempt +// to use the protobuf library) to verify that the version you link against +// matches the headers you compiled against. If a version mismatch is +// detected, the process will abort. +#define GOOGLE_PROTOBUF_VERIFY_VERSION \ + ::google::protobuf::internal::VerifyVersion( \ + GOOGLE_PROTOBUF_VERSION, GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION, \ + __FILE__) + +// =================================================================== +// from google3/base/port.h + +typedef unsigned int uint; + +#ifdef _MSC_VER +typedef __int8 int8; +typedef __int16 int16; +typedef __int32 int32; +typedef __int64 int64; + +typedef unsigned __int8 uint8; +typedef unsigned __int16 uint16; +typedef unsigned __int32 uint32; +typedef unsigned __int64 uint64; +#else +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +#endif + +// long long macros to be used because gcc and vc++ use different suffixes, +// and different size specifiers in format strings +#undef GOOGLE_LONGLONG +#undef GOOGLE_ULONGLONG +#undef GOOGLE_LL_FORMAT + +#ifdef _MSC_VER +#define GOOGLE_LONGLONG(x) x##I64 +#define GOOGLE_ULONGLONG(x) x##UI64 +#define GOOGLE_LL_FORMAT "I64" // As in printf("%I64d", ...) +#else +#define GOOGLE_LONGLONG(x) x##LL +#define GOOGLE_ULONGLONG(x) x##ULL +#define GOOGLE_LL_FORMAT "ll" // As in "%lld". Note that "q" is poor form also. +#endif + +static const int32 kint32max = 0x7FFFFFFF; +static const int32 kint32min = -kint32min - 1; +static const int64 kint64max = GOOGLE_LONGLONG(0x7FFFFFFFFFFFFFFF); +static const int64 kint64min = -kint64max - 1; +static const uint32 kuint32max = 0xFFFFFFFFu; +static const uint64 kuint64max = GOOGLE_ULONGLONG(0xFFFFFFFFFFFFFFFF); + +#undef GOOGLE_ATTRIBUTE_ALWAYS_INLINE +#if defined(__GNUC__) && (__GNUC__ > 3 ||(__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +// For functions we want to force inline. +// Introduced in gcc 3.1. +#define GOOGLE_ATTRIBUTE_ALWAYS_INLINE __attribute__ ((always_inline)) +#else +// Other compilers will have to figure it out for themselves. +#define GOOGLE_ATTRIBUTE_ALWAYS_INLINE +#endif + +// =================================================================== +// from google3/base/basictypes.h + +// The GOOGLE_ARRAYSIZE(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. +// +// GOOGLE_ARRAYSIZE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using GOOGLE_ARRAYSIZE, you are (wrongfully) giving it a pointer. +// You should only use GOOGLE_ARRAYSIZE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. +// +// Kudos to Jorg Brown for this simple and elegant implementation. + +#undef GOOGLE_ARRAYSIZE +#define GOOGLE_ARRAYSIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) + +namespace internal { + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertable to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template +inline To implicit_cast(From const &f) { + return f; +} + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. + +template // use like this: down_cast(foo); +inline To down_cast(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + if (false) { + implicit_cast(0); + } + + assert(f == NULL || dynamic_cast(f) != NULL); // RTTI: debug mode only! + return static_cast(f); +} + +} // 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; + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +namespace internal { + +template +struct CompileAssert { +}; + +} // namespace internal + +#undef GOOGLE_COMPILE_ASSERT +#define GOOGLE_COMPILE_ASSERT(expr, msg) \ + typedef ::google::protobuf::internal::CompileAssert<(bool(expr))> \ + msg[bool(expr) ? 1 : -1] + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// =================================================================== +// from google3/base/scoped_ptr.h + +namespace internal { + +// This is an implementation designed to match the anticipated future TR2 +// implementation of the scoped_ptr class, and its closely-related brethren, +// scoped_array, scoped_ptr_malloc, and make_scoped_ptr. + +template class scoped_ptr; +template class scoped_array; + +// A scoped_ptr is like a T*, except that the destructor of scoped_ptr +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr owns the T object that it points to. +// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. +// +// The size of a scoped_ptr is small: +// sizeof(scoped_ptr) == sizeof(C*) +template +class scoped_ptr { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_ptr. + // The input parameter must be allocated with new. + explicit scoped_ptr(C* p = NULL) : ptr_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_ptr() { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != ptr_) { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + ptr_ = p; + } + } + + // Accessors to get the owned object. + // operator* and operator-> will assert() if there is no current object. + C& operator*() const { + assert(ptr_ != NULL); + return *ptr_; + } + C* operator->() const { + assert(ptr_ != NULL); + return ptr_; + } + C* get() const { return ptr_; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return ptr_ == p; } + bool operator!=(C* p) const { return ptr_ != p; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + C* tmp = ptr_; + ptr_ = p2.ptr_; + p2.ptr_ = tmp; + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = ptr_; + ptr_ = NULL; + return retVal; + } + + private: + C* ptr_; + + // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't + // make sense, and if C2 == C, it still doesn't make sense because you should + // never have the same object owned by two different scoped_ptrs. + template bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; + + // Disallow evil constructors + scoped_ptr(const scoped_ptr&); + void operator=(const scoped_ptr&); +}; + +// scoped_array is like scoped_ptr, except that the caller must allocate +// with new [] and the destructor deletes objects with delete []. +// +// As with scoped_ptr, a scoped_array either points to an object +// or is NULL. A scoped_array owns the object that it points to. +// +// Size: sizeof(scoped_array) == sizeof(C*) +template +class scoped_array { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_array. + // The input parameter must be allocated with new []. + explicit scoped_array(C* p = NULL) : array_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_array() { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != array_) { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + array_ = p; + } + } + + // Get one element of the current object. + // Will assert() if there is no current object, or index i is negative. + C& operator[](std::ptrdiff_t i) const { + assert(i >= 0); + assert(array_ != NULL); + return array_[i]; + } + + // Get a pointer to the zeroth element of the current object. + // If there is no current object, return NULL. + C* get() const { + return array_; + } + + // Comparison operators. + // These return whether two scoped_array refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return array_ == p; } + bool operator!=(C* p) const { return array_ != p; } + + // Swap two scoped arrays. + void swap(scoped_array& p2) { + C* tmp = array_; + array_ = p2.array_; + p2.array_ = tmp; + } + + // Release an array. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = array_; + array_ = NULL; + return retVal; + } + + private: + C* array_; + + // Forbid comparison of different scoped_array types. + template bool operator==(scoped_array const& p2) const; + template bool operator!=(scoped_array const& p2) const; + + // Disallow evil constructors + scoped_array(const scoped_array&); + void operator=(const scoped_array&); +}; + +} // 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::scoped_ptr; +using internal::scoped_array; + +// =================================================================== +// emulates google3/base/logging.h + +enum LogLevel { + LOGLEVEL_INFO, // Informational. This is never actually used by + // libprotobuf. + LOGLEVEL_WARNING, // Warns about issues that, although not technically a + // problem now, could cause problems in the future. For + // example, a // warning will be printed when parsing a + // message that is near the message size limit. + LOGLEVEL_ERROR, // An error occurred which should never happen during + // normal use. + LOGLEVEL_FATAL, // An error occurred from which the library cannot + // recover. This usually indicates a programming error + // in the code which calls the library, especially when + // compiled in debug mode. + +#ifdef NDEBUG + LOGLEVEL_DFATAL = LOGLEVEL_ERROR +#else + LOGLEVEL_DFATAL = LOGLEVEL_FATAL +#endif +}; + +namespace internal { + +class LogFinisher; + +class LIBPROTOBUF_EXPORT LogMessage { + public: + LogMessage(LogLevel level, const char* filename, int line); + ~LogMessage(); + + LogMessage& operator<<(const string& value); + LogMessage& operator<<(const char* value); + LogMessage& operator<<(char value); + LogMessage& operator<<(int value); + LogMessage& operator<<(uint value); + LogMessage& operator<<(double value); + + private: + friend class LogFinisher; + void Finish(); + + LogLevel level_; + const char* filename_; + int line_; + string message_; +}; + +// Used to make the entire "LOG(BLAH) << etc." expression have a void return +// type and print a newline after each message. +class LIBPROTOBUF_EXPORT LogFinisher { + public: + void operator=(LogMessage& other); +}; + +} // namespace internal + +// Undef everything in case we're being mixed with some other Google library +// which already defined them itself. Presumably all Google libraries will +// support the same syntax for these so it should not be a big deal if they +// end up using our definitions instead. +#undef GOOGLE_LOG +#undef GOOGLE_LOG_IF + +#undef GOOGLE_CHECK +#undef GOOGLE_CHECK_EQ +#undef GOOGLE_CHECK_NE +#undef GOOGLE_CHECK_LT +#undef GOOGLE_CHECK_LE +#undef GOOGLE_CHECK_GT +#undef GOOGLE_CHECK_GE + +#undef GOOGLE_DLOG +#undef GOOGLE_DCHECK +#undef GOOGLE_DCHECK_EQ +#undef GOOGLE_DCHECK_NE +#undef GOOGLE_DCHECK_LT +#undef GOOGLE_DCHECK_LE +#undef GOOGLE_DCHECK_GT +#undef GOOGLE_DCHECK_GE + +#define GOOGLE_LOG(LEVEL) \ + ::google::protobuf::internal::LogFinisher() = \ + ::google::protobuf::internal::LogMessage( \ + ::google::protobuf::LOGLEVEL_##LEVEL, __FILE__, __LINE__) +#define GOOGLE_LOG_IF(LEVEL, CONDITION) \ + !(CONDITION) ? (void)0 : GOOGLE_LOG(LEVEL) + +#define GOOGLE_CHECK(EXPRESSION) \ + GOOGLE_LOG_IF(FATAL, !(EXPRESSION)) << "CHECK failed: " #EXPRESSION ": " +#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) +#define GOOGLE_CHECK_LE(A, B) GOOGLE_CHECK(A <= B) +#define GOOGLE_CHECK_GT(A, B) GOOGLE_CHECK(A > B) +#define GOOGLE_CHECK_GE(A, B) GOOGLE_CHECK(A >= B) + +#ifdef NDEBUG + +#define GOOGLE_DLOG GOOGLE_LOG_IF(false, INFO) + +#define GOOGLE_DCHECK(EXPRESSION) while(false) GOOGLE_CHECK(EXPRESSION) +#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) +#define GOOGLE_DCHECK_LE(A, B) GOOGLE_DCHECK(A <= B) +#define GOOGLE_DCHECK_GT(A, B) GOOGLE_DCHECK(A > B) +#define GOOGLE_DCHECK_GE(A, B) GOOGLE_DCHECK(A >= B) + +#else // NDEBUG + +#define GOOGLE_DLOG GOOGLE_LOG + +#define GOOGLE_DCHECK GOOGLE_CHECK +#define GOOGLE_DCHECK_EQ GOOGLE_CHECK_EQ +#define GOOGLE_DCHECK_NE GOOGLE_CHECK_NE +#define GOOGLE_DCHECK_LT GOOGLE_CHECK_LT +#define GOOGLE_DCHECK_LE GOOGLE_CHECK_LE +#define GOOGLE_DCHECK_GT GOOGLE_CHECK_GT +#define GOOGLE_DCHECK_GE GOOGLE_CHECK_GE + +#endif // !NDEBUG + +typedef void LogHandler(LogLevel level, const char* filename, int line, + const string& message); + +// The protobuf library sometimes writes warning and error messages to +// stderr. These messages are primarily useful for developers, but may +// also help end users figure out a problem. If you would prefer that +// these messages be sent somewhere other than stderr, call SetLogHandler() +// to set your own handler. This returns the old handler. Set the handler +// to NULL to ignore log messages (but see also LogSilencer, below). +// +// Obviously, SetLogHandler is not thread-safe. You should only call it +// at initialization time, and probably not from library code. If you +// simply want to suppress log messages temporarily (e.g. because you +// have some code that tends to trigger them frequently and you know +// the warnings are not important to you), use the LogSilencer class +// below. +LIBPROTOBUF_EXPORT LogHandler* SetLogHandler(LogHandler* new_func); + +// Create a LogSilencer if you want to temporarily suppress all log +// messages. As long as any LogSilencer objects exist, non-fatal +// log messages will be discarded (the current LogHandler will *not* +// be called). Constructing a LogSilencer is thread-safe. You may +// accidentally suppress log messages occurring in another thread, but +// since messages are generally for debugging purposes only, this isn't +// a big deal. If you want to intercept log messages, use SetLogHandler(). +class LIBPROTOBUF_EXPORT LogSilencer { + public: + LogSilencer(); + ~LogSilencer(); +}; + +// =================================================================== +// emulates google3/base/callback.h + +// Abstract interface for a callback. When calling an RPC, you must provide +// a Closure to call when the procedure completes. See the Service interface +// in service.h. +// +// To automatically construct a Closure which calls a particular function or +// method with a particular set of parameters, use the NewCallback() function. +// Example: +// void FooDone(const FooResponse* response) { +// ... +// } +// +// void CallFoo() { +// ... +// // When done, call FooDone() and pass it a pointer to the response. +// Closure* callback = NewCallback(&FooDone, response); +// // Make the call. +// service->Foo(controller, request, response, callback); +// } +// +// Example that calls a method: +// class Handler { +// public: +// ... +// +// void FooDone(const FooResponse* response) { +// ... +// } +// +// void CallFoo() { +// ... +// // When done, call FooDone() and pass it a pointer to the response. +// Closure* callback = NewCallback(this, &Handler::FooDone, response); +// // Make the call. +// service->Foo(controller, request, response, callback); +// } +// }; +// +// Currently NewCallback() supports binding zero, one, or two arguments. +// +// Callbacks created with NewCallback() automatically delete themselves when +// executed. They should be used when a callback is to be called exactly +// once (usually the case with RPC callbacks). If a callback may be called +// a different number of times (including zero), create it with +// NewPermanentCallback() instead. You are then responsible for deleting the +// callback (using the "delete" keyword as normal). +// +// Note that NewCallback() is a bit touchy regarding argument types. Generally, +// the values you provide for the parameter bindings must exactly match the +// types accepted by the callback function. For example: +// void Foo(string s); +// NewCallback(&Foo, "foo"); // WON'T WORK: const char* != string +// NewCallback(&Foo, string("foo")); // WORKS +// Also note that the arguments cannot be references: +// void Foo(const string& s); +// string my_str; +// NewCallback(&Foo, my_str); // WON'T WORK: Can't use referecnes. +// However, correctly-typed pointers will work just fine. +class LIBPROTOBUF_EXPORT Closure { + public: + Closure() {} + virtual ~Closure(); + + virtual void Run() = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Closure); +}; + +namespace internal { + +class LIBPROTOBUF_EXPORT FunctionClosure0 : public Closure { + public: + typedef void (*FunctionType)(); + + FunctionClosure0(FunctionType function, bool self_deleting) + : function_(function), self_deleting_(self_deleting) {} + ~FunctionClosure0(); + + void Run() { + function_(); + if (self_deleting_) delete this; + } + + private: + FunctionType function_; + bool self_deleting_; +}; + +template +class MethodClosure0 : public Closure { + public: + typedef void (Class::*MethodType)(); + + MethodClosure0(Class* object, MethodType method, bool self_deleting) + : object_(object), method_(method), self_deleting_(self_deleting) {} + ~MethodClosure0() {} + + void Run() { + (object_->*method_)(); + if (self_deleting_) delete this; + } + + private: + Class* object_; + MethodType method_; + bool self_deleting_; +}; + +template +class FunctionClosure1 : public Closure { + public: + typedef void (*FunctionType)(Arg1 arg1); + + FunctionClosure1(FunctionType function, bool self_deleting, + Arg1 arg1) + : function_(function), self_deleting_(self_deleting), + arg1_(arg1) {} + ~FunctionClosure1() {} + + void Run() { + function_(arg1_); + if (self_deleting_) delete this; + } + + private: + FunctionType function_; + bool self_deleting_; + Arg1 arg1_; +}; + +template +class MethodClosure1 : public Closure { + public: + typedef void (Class::*MethodType)(Arg1 arg1); + + MethodClosure1(Class* object, MethodType method, bool self_deleting, + Arg1 arg1) + : object_(object), method_(method), self_deleting_(self_deleting), + arg1_(arg1) {} + ~MethodClosure1() {} + + void Run() { + (object_->*method_)(arg1_); + if (self_deleting_) delete this; + } + + private: + Class* object_; + MethodType method_; + bool self_deleting_; + Arg1 arg1_; +}; + +template +class FunctionClosure2 : public Closure { + public: + typedef void (*FunctionType)(Arg1 arg1, Arg2 arg2); + + FunctionClosure2(FunctionType function, bool self_deleting, + Arg1 arg1, Arg2 arg2) + : function_(function), self_deleting_(self_deleting), + arg1_(arg1), arg2_(arg2) {} + ~FunctionClosure2() {} + + void Run() { + function_(arg1_, arg2_); + if (self_deleting_) delete this; + } + + private: + FunctionType function_; + bool self_deleting_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +class MethodClosure2 : public Closure { + public: + typedef void (Class::*MethodType)(Arg1 arg1, Arg2 arg2); + + MethodClosure2(Class* object, MethodType method, bool self_deleting, + Arg1 arg1, Arg2 arg2) + : object_(object), method_(method), self_deleting_(self_deleting), + arg1_(arg1), arg2_(arg2) {} + ~MethodClosure2() {} + + void Run() { + (object_->*method_)(arg1_, arg2_); + if (self_deleting_) delete this; + } + + private: + Class* object_; + MethodType method_; + bool self_deleting_; + Arg1 arg1_; + Arg2 arg2_; +}; + +} // namespace internal + +// See Closure. +inline Closure* NewCallback(void (*function)()) { + return new internal::FunctionClosure0(function, true); +} + +// See Closure. +inline Closure* NewPermanentCallback(void (*function)()) { + return new internal::FunctionClosure0(function, false); +} + +// See Closure. +template +inline Closure* NewCallback(Class* object, void (Class::*method)()) { + return new internal::MethodClosure0(object, method, true); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(Class* object, void (Class::*method)()) { + return new internal::MethodClosure0(object, method, false); +} + +// See Closure. +template +inline Closure* NewCallback(void (*function)(Arg1), + Arg1 arg1) { + return new internal::FunctionClosure1(function, true, arg1); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(void (*function)(Arg1), + Arg1 arg1) { + return new internal::FunctionClosure1(function, false, arg1); +} + +// See Closure. +template +inline Closure* NewCallback(Class* object, void (Class::*method)(Arg1), + Arg1 arg1) { + return new internal::MethodClosure1(object, method, true, arg1); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(Class* object, void (Class::*method)(Arg1), + Arg1 arg1) { + return new internal::MethodClosure1(object, method, false, arg1); +} + +// See Closure. +template +inline Closure* NewCallback(void (*function)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::FunctionClosure2( + function, true, arg1, arg2); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(void (*function)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::FunctionClosure2( + function, false, arg1, arg2); +} + +// See Closure. +template +inline Closure* NewCallback(Class* object, void (Class::*method)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::MethodClosure2( + object, method, true, arg1, arg2); +} + +// See Closure. +template +inline Closure* NewPermanentCallback( + Class* object, void (Class::*method)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::MethodClosure2( + object, method, false, arg1, arg2); +} + +// A function which does nothing. Useful for creating no-op callbacks, e.g.: +// Closure* nothing = NewCallback(&DoNothing); +void LIBPROTOBUF_EXPORT DoNothing(); + +// =================================================================== +// emulates google3/base/mutex.h + +namespace internal { + +// A Mutex is a non-reentrant (aka non-recursive) mutex. At most one thread T +// may hold a mutex at a given time. If T attempts to Lock() the same Mutex +// while holding it, T will deadlock. +class LIBPROTOBUF_EXPORT Mutex { + public: + // Create a Mutex that is not held by anybody. + Mutex(); + + // Destructor + ~Mutex(); + + // Block if necessary until this Mutex is free, then acquire it exclusively. + void Lock(); + + // Release this Mutex. Caller must hold it exclusively. + void Unlock(); + + // Crash if this Mutex is not held exclusively by this thread. + // May fail to crash when it should; will never crash when it should not. + void AssertHeld(); + + private: + struct Internal; + Internal* mInternal; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Mutex); +}; + +// MutexLock(mu) acquires mu when constructed and releases it when destroyed. +class LIBPROTOBUF_EXPORT MutexLock { + public: + explicit MutexLock(Mutex *mu) : mu_(mu) { this->mu_->Lock(); } + ~MutexLock() { this->mu_->Unlock(); } + private: + Mutex *const mu_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MutexLock); +}; + +// MutexLockMaybe is like MutexLock, but is a no-op when mu is NULL. +class LIBPROTOBUF_EXPORT MutexLockMaybe { + public: + explicit MutexLockMaybe(Mutex *mu) : + mu_(mu) { if (this->mu_ != NULL) { this->mu_->Lock(); } } + ~MutexLockMaybe() { if (this->mu_ != NULL) { this->mu_->Unlock(); } } + private: + Mutex *const mu_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MutexLockMaybe); +}; + +} // 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::Mutex; +using internal::MutexLock; +using internal::MutexLockMaybe; + +// =================================================================== +// from google3/base/type_traits.h + +namespace internal { + +// Specified by TR1 [4.7.4] Pointer modifications. +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { + typedef T type; }; + +} // namespace internal + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMMON_H__ diff --git a/src/google/protobuf/stubs/common_unittest.cc b/src/google/protobuf/stubs/common_unittest.cc new file mode 100644 index 00000000..f12422be --- /dev/null +++ b/src/google/protobuf/stubs/common_unittest.cc @@ -0,0 +1,319 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include +#include + +#include +#include + +#include "config.h" + +namespace google { +namespace protobuf { +namespace { + +// TODO(kenton): More tests. + +#ifdef PACKAGE_VERSION // only defined when using automake, not MSVC + +TEST(VersionTest, VersionMatchesConfig) { + // Verify that the version string specified in config.h matches the one + // in common.h. The config.h version is a string which may have a suffix + // like "beta", so we remove that. + string version = PACKAGE_VERSION; + int pos = version.size(); + while (pos > 0 && !ascii_isdigit(version[pos-1])) { + --pos; + } + version.erase(pos); + + EXPECT_EQ(version, internal::VersionString(GOOGLE_PROTOBUF_VERSION)); +} + +#endif // PACKAGE_VERSION + +vector captured_messages_; + +void CaptureLog(LogLevel level, const char* filename, int line, + const string& message) { + captured_messages_.push_back( + strings::Substitute("$0 $1:$2: $3", + implicit_cast(level), filename, line, message)); +} + +TEST(LoggingTest, DefaultLogging) { + CaptureTestStderr(); + int line = __LINE__; + GOOGLE_LOG(INFO ) << "A message."; + GOOGLE_LOG(WARNING) << "A warning."; + GOOGLE_LOG(ERROR ) << "An error."; + + string text = GetCapturedTestStderr(); + EXPECT_EQ( + "libprotobuf INFO "__FILE__":" + SimpleItoa(line + 1) + "] A message.\n" + "libprotobuf WARNING "__FILE__":" + SimpleItoa(line + 2) + "] A warning.\n" + "libprotobuf ERROR "__FILE__":" + SimpleItoa(line + 3) + "] An error.\n", + text); +} + +TEST(LoggingTest, NullLogging) { + LogHandler* old_handler = SetLogHandler(NULL); + + CaptureTestStderr(); + GOOGLE_LOG(INFO ) << "A message."; + GOOGLE_LOG(WARNING) << "A warning."; + GOOGLE_LOG(ERROR ) << "An error."; + + EXPECT_TRUE(SetLogHandler(old_handler) == NULL); + + string text = GetCapturedTestStderr(); + EXPECT_EQ("", text); +} + +TEST(LoggingTest, CaptureLogging) { + captured_messages_.clear(); + + LogHandler* old_handler = SetLogHandler(&CaptureLog); + + int start_line = __LINE__; + GOOGLE_LOG(ERROR) << "An error."; + GOOGLE_LOG(WARNING) << "A warning."; + + EXPECT_TRUE(SetLogHandler(old_handler) == &CaptureLog); + + ASSERT_EQ(2, captured_messages_.size()); + EXPECT_EQ( + "2 "__FILE__":" + SimpleItoa(start_line + 1) + ": An error.", + captured_messages_[0]); + EXPECT_EQ( + "1 "__FILE__":" + SimpleItoa(start_line + 2) + ": A warning.", + captured_messages_[1]); +} + +TEST(LoggingTest, SilenceLogging) { + captured_messages_.clear(); + + LogHandler* old_handler = SetLogHandler(&CaptureLog); + + int line1 = __LINE__; GOOGLE_LOG(INFO) << "Visible1"; + LogSilencer* silencer1 = new LogSilencer; + GOOGLE_LOG(INFO) << "Not visible."; + LogSilencer* silencer2 = new LogSilencer; + GOOGLE_LOG(INFO) << "Not visible."; + delete silencer1; + GOOGLE_LOG(INFO) << "Not visible."; + delete silencer2; + int line2 = __LINE__; GOOGLE_LOG(INFO) << "Visible2"; + + EXPECT_TRUE(SetLogHandler(old_handler) == &CaptureLog); + + ASSERT_EQ(2, captured_messages_.size()); + EXPECT_EQ( + "0 "__FILE__":" + SimpleItoa(line1) + ": Visible1", + captured_messages_[0]); + EXPECT_EQ( + "0 "__FILE__":" + SimpleItoa(line2) + ": Visible2", + captured_messages_[1]); +} + +class ClosureTest : public testing::Test { + public: + void SetA123Method() { a_ = 123; } + static void SetA123Function() { current_instance_->a_ = 123; } + + void SetAMethod(int a) { a_ = a; } + void SetCMethod(string c) { c_ = c; } + + static void SetAFunction(int a) { current_instance_->a_ = a; } + static void SetCFunction(string c) { current_instance_->c_ = c; } + + void SetABMethod(int a, const char* b) { a_ = a; b_ = b; } + static void SetABFunction(int a, const char* b) { + current_instance_->a_ = a; + current_instance_->b_ = b; + } + + virtual void SetUp() { + current_instance_ = this; + a_ = 0; + b_ = NULL; + c_.clear(); + } + + int a_; + const char* b_; + string c_; + + static ClosureTest* current_instance_; +}; + +ClosureTest* ClosureTest::current_instance_ = NULL; + +TEST_F(ClosureTest, TestClosureFunction0) { + Closure* closure = NewCallback(&SetA123Function); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); +} + +TEST_F(ClosureTest, TestClosureMethod0) { + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetA123Method); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); +} + +TEST_F(ClosureTest, TestClosureFunction1) { + Closure* closure = NewCallback(&SetAFunction, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); +} + +TEST_F(ClosureTest, TestClosureMethod1) { + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetAMethod, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); +} + +TEST_F(ClosureTest, TestClosureFunction1String) { + Closure* closure = NewCallback(&SetCFunction, string("test")); + EXPECT_NE("test", c_); + closure->Run(); + EXPECT_EQ("test", c_); +} + +TEST_F(ClosureTest, TestClosureMethod1String) { + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetCMethod, string("test")); + EXPECT_NE("test", c_); + closure->Run(); + EXPECT_EQ("test", c_); +} + +TEST_F(ClosureTest, TestClosureFunction2) { + const char* cstr = "hello"; + Closure* closure = NewCallback(&SetABFunction, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); +} + +TEST_F(ClosureTest, TestClosureMethod2) { + const char* cstr = "hello"; + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetABMethod, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); +} + +// Repeat all of the above with NewPermanentCallback() + +TEST_F(ClosureTest, TestPermanentClosureFunction0) { + Closure* closure = NewPermanentCallback(&SetA123Function); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(123, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureMethod0) { + Closure* closure = NewPermanentCallback(current_instance_, + &ClosureTest::SetA123Method); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(123, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureFunction1) { + Closure* closure = NewPermanentCallback(&SetAFunction, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(456, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureMethod1) { + Closure* closure = NewPermanentCallback(current_instance_, + &ClosureTest::SetAMethod, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(456, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureFunction2) { + const char* cstr = "hello"; + Closure* closure = NewPermanentCallback(&SetABFunction, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + a_ = 0; + b_ = NULL; + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureMethod2) { + const char* cstr = "hello"; + Closure* closure = NewPermanentCallback(current_instance_, + &ClosureTest::SetABMethod, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + a_ = 0; + b_ = NULL; + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + delete closure; +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/hash.cc b/src/google/protobuf/stubs/hash.cc new file mode 100644 index 00000000..43fb9d73 --- /dev/null +++ b/src/google/protobuf/stubs/hash.cc @@ -0,0 +1,27 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) + +#include + +namespace google { +namespace protobuf { + +// Nothing needed here right now. + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/hash.h b/src/google/protobuf/stubs/hash.h new file mode 100644 index 00000000..a62b3f6e --- /dev/null +++ b/src/google/protobuf/stubs/hash.h @@ -0,0 +1,123 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// +// Deals with the fact that hash_map is not defined everywhere. + +#ifndef GOOGLE_PROTOBUF_STUBS_HASH_H__ +#define GOOGLE_PROTOBUF_STUBS_HASH_H__ + +#include +#include +#include "config.h" + +#if defined(HAVE_HASH_MAP) && defined(HAVE_HASH_SET) +#include HASH_MAP_H +#include HASH_SET_H +#else +// TODO(kenton): Deal with non-existence of hash_map somehow. Maybe emulate +// it with map? +#error "Your STL implementation lacks hash_map and/or hash_set." +#endif + +namespace google { +namespace protobuf { + +#ifdef _MSC_VER + +template +struct hash : public HASH_NAMESPACE::hash_compare { +}; + +// MSVC's hash_compare hashes based on the string contents but +// compares based on the string pointer. WTF? +class CstringLess { + public: + inline bool operator()(const char* a, const char* b) const { + return strcmp(a, b) < 0; + } +}; + +template <> +struct hash + : public HASH_NAMESPACE::hash_compare { +}; + +template , + typename EqualKey = int > +class hash_map : public HASH_NAMESPACE::hash_map< + Key, Data, HashFcn> { +}; + +template , + typename EqualKey = int > +class hash_set : public HASH_NAMESPACE::hash_set< + Key, HashFcn> { +}; + +#else + +template +struct hash : public HASH_NAMESPACE::hash { +}; + +template +struct hash { + inline size_t operator()(const Key* key) const { + return reinterpret_cast(key); + } +}; + +template <> +struct hash : public HASH_NAMESPACE::hash { +}; + +template , + typename EqualKey = std::equal_to > +class hash_map : public HASH_NAMESPACE::hash_map< + Key, Data, HashFcn, EqualKey> { +}; + +template , + typename EqualKey = std::equal_to > +class hash_set : public HASH_NAMESPACE::hash_set< + Key, HashFcn, EqualKey> { +}; + +#endif + +template <> +struct hash { + inline size_t operator()(const string& key) const { + return hash()(key.c_str()); + } + + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + inline size_t operator()(const string& a, const string& b) const { + return a < b; + } +}; + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_HASH_H__ diff --git a/src/google/protobuf/stubs/map-util.cc b/src/google/protobuf/stubs/map-util.cc new file mode 100644 index 00000000..af05af30 --- /dev/null +++ b/src/google/protobuf/stubs/map-util.cc @@ -0,0 +1,28 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// from google3/util/gtl/map-util.cc +// Author: Anton Carver + +#include + +namespace google { +namespace protobuf { + +// Template module. Nothing to see here. + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/map-util.h b/src/google/protobuf/stubs/map-util.h new file mode 100644 index 00000000..ee8073fe --- /dev/null +++ b/src/google/protobuf/stubs/map-util.h @@ -0,0 +1,90 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// from google3/util/gtl/map-util.h +// Author: Anton Carver + +#ifndef GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__ +#define GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__ + +#include + +namespace google { +namespace protobuf { + +// Perform a lookup in a map or hash_map. +// If the key is present a const pointer to the associated value is returned, +// otherwise a NULL pointer is returned. +template +const typename Collection::value_type::second_type* +FindOrNull(const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::const_iterator it = collection.find(key); + if (it == collection.end()) { + return 0; + } + return &it->second; +} + +// Perform a lookup in a map or hash_map whose values are pointers. +// If the key is present a const pointer to the associated value is returned, +// otherwise a NULL pointer is returned. +// This function does not distinguish between a missing key and a key mapped +// to a NULL value. +template +const typename Collection::value_type::second_type +FindPtrOrNull(const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::const_iterator it = collection.find(key); + if (it == collection.end()) { + return 0; + } + return it->second; +} + +// Change the value associated with a particular key in a map or hash_map. +// If the key is not present in the map the key and value are inserted, +// otherwise the value is updated to be a copy of the value provided. +// True indicates that an insert took place, false indicates an update. +template +bool InsertOrUpdate(Collection * const collection, + const Key& key, const Value& value) { + pair ret = + collection->insert(typename Collection::value_type(key, value)); + if (!ret.second) { + // update + ret.first->second = value; + return false; + } + return true; +} + +// Insert a new key and value into a map or hash_map. +// If the key is not present in the map the key and value are +// inserted, otherwise nothing happens. True indicates that an insert +// took place, false indicates the key was already present. +template +bool InsertIfNotPresent(Collection * const collection, + const Key& key, const Value& value) { + pair ret = + collection->insert(typename Collection::value_type(key, value)); + return ret.second; +} + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__ diff --git a/src/google/protobuf/stubs/stl_util-inl.cc b/src/google/protobuf/stubs/stl_util-inl.cc new file mode 100644 index 00000000..445c646e --- /dev/null +++ b/src/google/protobuf/stubs/stl_util-inl.cc @@ -0,0 +1,27 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// from google3/util/gtl/stl_util-inl.cc + +#include + +namespace google { +namespace protobuf { + +// Template module. Nothing to see here. + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/stl_util-inl.h b/src/google/protobuf/stubs/stl_util-inl.h new file mode 100644 index 00000000..db079a77 --- /dev/null +++ b/src/google/protobuf/stubs/stl_util-inl.h @@ -0,0 +1,107 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// from google3/util/gtl/stl_util-inl.h + +#ifndef GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__ +#define GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__ + +#include + +namespace google { +namespace protobuf { + +// STLDeleteContainerPointers() +// For a range within a container of pointers, calls delete +// (non-array version) on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template +void STLDeleteContainerPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// Inside Google, this function implements a horrible, disgusting hack in which +// we reach into the string's private implementation and resize it without +// initializing the new bytes. In some cases doing this can significantly +// improve performance. However, since it's totally non-portable it has no +// place in open source code. Feel free to fill this function in with your +// own disgusting hack if you want the perf boost. +inline void STLStringResizeUninitialized(string* s, size_t new_size) { + s->resize(new_size); +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(string* str) { + // DO NOT USE const_cast(str->data())! See the unittest for why. + return str->empty() ? NULL : &*str->begin(); +} + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// ElementDeleter (defined below), which ensures that your container's elements +// are deleted when the ElementDeleter goes out of scope. +template +void STLDeleteElements(T *container) { + if (!container) return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. + +template +void STLDeleteValues(T *v) { + if (!v) return; + for (typename T::iterator i = v->begin(); i != v->end(); ++i) { + delete i->second; + } + v->clear(); +} + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__ diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc new file mode 100644 index 00000000..07caaf76 --- /dev/null +++ b/src/google/protobuf/stubs/strutil.cc @@ -0,0 +1,1121 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// from google3/strings/strutil.cc + +#include +#include +#include // FLT_DIG and DBL_DIG +#include +#include + +#ifdef _WIN32 +// MSVC has only _snprintf, not snprintf. +// +// MinGW has both snprintf and _snprintf, but they appear to be different +// functions. The former is buggy. When invoked like so: +// char buffer[32]; +// snprintf(buffer, 32, "%.*g\n", FLT_DIG, 1.23e10f); +// it prints "1.23000e+10". This is plainly wrong: %g should never print +// trailing zeros after the decimal point. For some reason this bug only +// occurs with some input values, not all. In any case, _snprintf does the +// right thing, so we use it. +#define snprintf _snprintf +#endif + +namespace google { +namespace protobuf { + +inline bool IsNaN(double value) { + // NaN is never equal to anything, even itself. + return value != value; +} + +// The definitions of these in ctype.h change based on locale. Since our +// string manipulation is all in relation to the protocol buffer and C++ +// languages, we always want to use the C locale. So, we re-define these +// exactly as we want them. +static bool isxdigit(char c) { + return ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'); +} + +static bool isprint(char c) { + return c >= 0x20 && c <= 0x7E; +} + +// ---------------------------------------------------------------------- +// StripString +// Replaces any occurrence of the character 'remove' (or the characters +// in 'remove') with the character 'replacewith'. +// ---------------------------------------------------------------------- +void StripString(string* s, const char* remove, char replacewith) { + const char * str_start = s->c_str(); + const char * str = str_start; + for (str = strpbrk(str, remove); + str != NULL; + str = strpbrk(str + 1, remove)) { + (*s)[str - str_start] = replacewith; + } +} + +// ---------------------------------------------------------------------- +// StringReplace() +// Replace the "old" pattern with the "new" pattern in a string, +// and append the result to "res". If replace_all is false, +// it only replaces the first instance of "old." +// ---------------------------------------------------------------------- + +void StringReplace(const string& s, const string& oldsub, + const string& newsub, bool replace_all, + string* res) { + if (oldsub.empty()) { + res->append(s); // if empty, append the given string. + return; + } + + string::size_type start_pos = 0; + string::size_type pos; + do { + pos = s.find(oldsub, start_pos); + if (pos == string::npos) { + break; + } + res->append(s, start_pos, pos - start_pos); + res->append(newsub); + start_pos = pos + oldsub.size(); // start searching again after the "old" + } while (replace_all); + res->append(s, start_pos, s.length() - start_pos); +} + +// ---------------------------------------------------------------------- +// StringReplace() +// Give me a string and two patterns "old" and "new", and I replace +// the first instance of "old" in the string with "new", if it +// exists. If "global" is true; call this repeatedly until it +// fails. RETURN a new string, regardless of whether the replacement +// happened or not. +// ---------------------------------------------------------------------- + +string StringReplace(const string& s, const string& oldsub, + const string& newsub, bool replace_all) { + string ret; + StringReplace(s, oldsub, newsub, replace_all, &ret); + return ret; +} + +// ---------------------------------------------------------------------- +// SplitStringUsing() +// Split a string using a character delimiter. Append the components +// to 'result'. +// +// Note: For multi-character delimiters, this routine will split on *ANY* of +// the characters in the string, not the entire string as a single delimiter. +// ---------------------------------------------------------------------- +template +static inline +void SplitStringToIteratorUsing(const string& full, + const char* delim, + ITR& result) { + // Optimize the common case where delim is a single character. + if (delim[0] != '\0' && delim[1] == '\0') { + char c = delim[0]; + const char* p = full.data(); + const char* end = p + full.size(); + while (p != end) { + if (*p == c) { + ++p; + } else { + const char* start = p; + while (++p != end && *p != c); + *result++ = string(start, p - start); + } + } + return; + } + + string::size_type begin_index, end_index; + begin_index = full.find_first_not_of(delim); + while (begin_index != string::npos) { + end_index = full.find_first_of(delim, begin_index); + if (end_index == string::npos) { + *result++ = full.substr(begin_index); + return; + } + *result++ = full.substr(begin_index, (end_index - begin_index)); + begin_index = full.find_first_not_of(delim, end_index); + } +} + +void SplitStringUsing(const string& full, + const char* delim, + vector* result) { + back_insert_iterator< vector > it(*result); + SplitStringToIteratorUsing(full, delim, it); +} + +// ---------------------------------------------------------------------- +// JoinStrings() +// This merges a vector of string components with delim inserted +// as separaters between components. +// +// ---------------------------------------------------------------------- +template +static void JoinStringsIterator(const ITERATOR& start, + const ITERATOR& end, + const char* delim, + string* result) { + GOOGLE_CHECK(result != NULL); + result->clear(); + int delim_length = strlen(delim); + + // Precompute resulting length so we can reserve() memory in one shot. + int length = 0; + for (ITERATOR iter = start; iter != end; ++iter) { + if (iter != start) { + length += delim_length; + } + length += iter->size(); + } + result->reserve(length); + + // Now combine everything. + for (ITERATOR iter = start; iter != end; ++iter) { + if (iter != start) { + result->append(delim, delim_length); + } + result->append(iter->data(), iter->size()); + } +} + +void JoinStrings(const vector& components, + const char* delim, + string * result) { + JoinStringsIterator(components.begin(), components.end(), delim, result); +} + +// ---------------------------------------------------------------------- +// UnescapeCEscapeSequences() +// This does all the unescaping that C does: \ooo, \r, \n, etc +// Returns length of resulting string. +// The implementation of \x parses any positive number of hex digits, +// but it is an error if the value requires more than 8 bits, and the +// result is truncated to 8 bits. +// +// The second call stores its errors in a supplied string vector. +// If the string vector pointer is NULL, it reports the errors with LOG(). +// ---------------------------------------------------------------------- + +#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(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) + +int UnescapeCEscapeSequences(const char* source, char* dest) { + return UnescapeCEscapeSequences(source, dest, NULL); +} + +int UnescapeCEscapeSequences(const char* source, char* dest, + vector *errors) { + GOOGLE_DCHECK(errors == NULL) << "Error reporting not implemented."; + + char* d = dest; + const char* p = source; + + // Small optimization for case where source = dest and there's no escaping + while ( p == d && *p != '\0' && *p != '\\' ) + p++, d++; + + while (*p != '\0') { + if (*p != '\\') { + *d++ = *p++; + } else { + switch ( *++p ) { // skip past the '\\' + case '\0': + LOG_STRING(ERROR, errors) << "String cannot end with \\"; + *d = '\0'; + return d - dest; // we're done with p + case 'a': *d++ = '\a'; break; + case 'b': *d++ = '\b'; break; + case 'f': *d++ = '\f'; break; + case 'n': *d++ = '\n'; break; + case 'r': *d++ = '\r'; break; + case 't': *d++ = '\t'; break; + case 'v': *d++ = '\v'; break; + case '\\': *d++ = '\\'; break; + case '?': *d++ = '\?'; break; // \? Who knew? + case '\'': *d++ = '\''; break; + case '"': *d++ = '\"'; break; + case '0': case '1': case '2': case '3': // octal digit: 1 to 3 digits + case '4': case '5': case '6': case '7': { + char ch = *p - '0'; + if ( IS_OCTAL_DIGIT(p[1]) ) + ch = ch * 8 + *++p - '0'; + if ( IS_OCTAL_DIGIT(p[1]) ) // safe (and easy) to do this twice + ch = ch * 8 + *++p - '0'; // now points at last digit + *d++ = ch; + break; + } + case 'x': case 'X': { + if (!isxdigit(p[1])) { + if (p[1] == '\0') { + LOG_STRING(ERROR, errors) << "String cannot end with \\x"; + } else { + LOG_STRING(ERROR, errors) << + "\\x cannot be followed by non-hex digit: \\" << *p << p[1]; + } + break; + } + unsigned int ch = 0; + const char *hex_start = p; + while (isxdigit(p[1])) // arbitrarily many hex digits + ch = (ch << 4) + hex_digit_to_int(*++p); + if (ch > 0xFF) + LOG_STRING(ERROR, errors) << "Value of " << + "\\" << string(hex_start, p+1-hex_start) << " exceeds 8 bits"; + *d++ = ch; + break; + } +#if 0 // TODO(kenton): Support \u and \U? Requires runetochar(). + case 'u': { + // \uhhhh => convert 4 hex digits to UTF-8 + char32 rune = 0; + const char *hex_start = p; + for (int i = 0; i < 4; ++i) { + if (isxdigit(p[1])) { // Look one char ahead. + rune = (rune << 4) + hex_digit_to_int(*++p); // Advance p. + } else { + LOG_STRING(ERROR, errors) + << "\\u must be followed by 4 hex digits: \\" + << string(hex_start, p+1-hex_start); + break; + } + } + d += runetochar(d, &rune); + break; + } + case 'U': { + // \Uhhhhhhhh => convert 8 hex digits to UTF-8 + char32 rune = 0; + const char *hex_start = p; + for (int i = 0; i < 8; ++i) { + if (isxdigit(p[1])) { // Look one char ahead. + // Don't change rune until we're sure this + // is within the Unicode limit, but do advance p. + char32 newrune = (rune << 4) + hex_digit_to_int(*++p); + if (newrune > 0x10FFFF) { + LOG_STRING(ERROR, errors) + << "Value of \\" + << string(hex_start, p + 1 - hex_start) + << " exceeds Unicode limit (0x10FFFF)"; + break; + } else { + rune = newrune; + } + } else { + LOG_STRING(ERROR, errors) + << "\\U must be followed by 8 hex digits: \\" + << string(hex_start, p+1-hex_start); + break; + } + } + d += runetochar(d, &rune); + break; + } +#endif + default: + LOG_STRING(ERROR, errors) << "Unknown escape sequence: \\" << *p; + } + p++; // read past letter we escaped + } + } + *d = '\0'; + return d - dest; +} + +// ---------------------------------------------------------------------- +// UnescapeCEscapeString() +// This does the same thing as UnescapeCEscapeSequences, but creates +// a new string. The caller does not need to worry about allocating +// a dest buffer. This should be used for non performance critical +// tasks such as printing debug messages. It is safe for src and dest +// to be the same. +// +// The second call stores its errors in a supplied string vector. +// If the string vector pointer is NULL, it reports the errors with LOG(). +// +// In the first and second calls, the length of dest is returned. In the +// the third call, the new string is returned. +// ---------------------------------------------------------------------- +int UnescapeCEscapeString(const string& src, string* dest) { + return UnescapeCEscapeString(src, dest, NULL); +} + +int UnescapeCEscapeString(const string& src, string* dest, + vector *errors) { + scoped_array unescaped(new char[src.size() + 1]); + int len = UnescapeCEscapeSequences(src.c_str(), unescaped.get(), errors); + GOOGLE_CHECK(dest); + dest->assign(unescaped.get(), len); + return len; +} + +string UnescapeCEscapeString(const string& src) { + scoped_array unescaped(new char[src.size() + 1]); + int len = UnescapeCEscapeSequences(src.c_str(), unescaped.get(), NULL); + return string(unescaped.get(), len); +} + +// ---------------------------------------------------------------------- +// CEscapeString() +// CHexEscapeString() +// Copies 'src' to 'dest', escaping dangerous characters using +// C-style escape sequences. This is very useful for preparing query +// flags. 'src' and 'dest' should not overlap. The 'Hex' version uses +// hexadecimal rather than octal sequences. +// Returns the number of bytes written to 'dest' (not including the \0) +// or -1 if there was insufficient space. +// +// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// ---------------------------------------------------------------------- +static int CEscapeInternal(const char* src, int src_len, char* dest, + int dest_len, bool use_hex) { + const char* src_end = src + src_len; + int used = 0; + bool last_hex_escape = false; // true if last output char was \xNN + + for (; src < src_end; src++) { + if (dest_len - used < 2) // Need space for two letter escape + return -1; + + bool is_hex_escape = false; + switch (*src) { + case '\n': dest[used++] = '\\'; dest[used++] = 'n'; break; + case '\r': dest[used++] = '\\'; dest[used++] = 'r'; break; + case '\t': dest[used++] = '\\'; dest[used++] = 't'; break; + case '\"': dest[used++] = '\\'; dest[used++] = '\"'; break; + case '\'': dest[used++] = '\\'; dest[used++] = '\''; break; + case '\\': dest[used++] = '\\'; dest[used++] = '\\'; break; + default: + // Note that if we emit \xNN and the src character after that is a hex + // digit then that digit must be escaped too to prevent it being + // interpreted as part of the character code by C. + if (!isprint(*src) || (last_hex_escape && isxdigit(*src))) { + if (dest_len - used < 4) // need space for 4 letter escape + return -1; + sprintf(dest + used, (use_hex ? "\\x%02x" : "\\%03o"), + static_cast(*src)); + is_hex_escape = use_hex; + used += 4; + } else { + dest[used++] = *src; break; + } + } + last_hex_escape = is_hex_escape; + } + + if (dest_len - used < 1) // make sure that there is room for \0 + return -1; + + dest[used] = '\0'; // doesn't count towards return value though + return used; +} + +int CEscapeString(const char* src, int src_len, char* dest, int dest_len) { + return CEscapeInternal(src, src_len, dest, dest_len, false); +} + +// ---------------------------------------------------------------------- +// CEscape() +// CHexEscape() +// Copies 'src' to result, escaping dangerous characters using +// C-style escape sequences. This is very useful for preparing query +// flags. 'src' and 'dest' should not overlap. The 'Hex' version +// hexadecimal rather than octal sequences. +// +// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// ---------------------------------------------------------------------- +string CEscape(const string& src) { + const int dest_length = src.size() * 4 + 1; // Maximum possible expansion + scoped_array dest(new char[dest_length]); + const int len = CEscapeInternal(src.data(), src.size(), + dest.get(), dest_length, false); + GOOGLE_DCHECK_GE(len, 0); + return string(dest.get(), len); +} + +// ---------------------------------------------------------------------- +// strto32_adaptor() +// strtou32_adaptor() +// Implementation of strto[u]l replacements that have identical +// overflow and underflow characteristics for both ILP-32 and LP-64 +// platforms, including errno preservation in error-free calls. +// ---------------------------------------------------------------------- + +int32 strto32_adaptor(const char *nptr, char **endptr, int base) { + const int saved_errno = errno; + errno = 0; + const long result = strtol(nptr, endptr, base); + if (errno == ERANGE && result == LONG_MIN) { + return kint32min; + } else if (errno == ERANGE && result == LONG_MAX) { + return kint32max; + } else if (errno == 0 && result < kint32min) { + errno = ERANGE; + return kint32min; + } else if (errno == 0 && result > kint32max) { + errno = ERANGE; + return kint32max; + } + if (errno == 0) + errno = saved_errno; + return static_cast(result); +} + +uint32 strtou32_adaptor(const char *nptr, char **endptr, int base) { + const int saved_errno = errno; + errno = 0; + const unsigned long result = strtoul(nptr, endptr, base); + if (errno == ERANGE && result == ULONG_MAX) { + return kuint32max; + } else if (errno == 0 && result > kuint32max) { + errno = ERANGE; + return kuint32max; + } + if (errno == 0) + errno = saved_errno; + return static_cast(result); +} + +// ---------------------------------------------------------------------- +// FastIntToBuffer() +// FastInt64ToBuffer() +// FastHexToBuffer() +// FastHex64ToBuffer() +// FastHex32ToBuffer() +// ---------------------------------------------------------------------- + +// Offset into buffer where FastInt64ToBuffer places the end of string +// null character. Also used by FastInt64ToBufferLeft. +static const int kFastInt64ToBufferOffset = 21; + +char *FastInt64ToBuffer(int64 i, char* buffer) { + // We could collapse the positive and negative sections, but that + // would be slightly slower for positive numbers... + // 22 bytes is enough to store -2**64, -18446744073709551616. + char* p = buffer + kFastInt64ToBufferOffset; + *p-- = '\0'; + if (i >= 0) { + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; + } else { + // On different platforms, % and / have different behaviors for + // negative numbers, so we need to jump through hoops to make sure + // we don't divide negative numbers. + if (i > -10) { + i = -i; + *p-- = '0' + i; + *p = '-'; + return p; + } else { + // Make sure we aren't at MIN_INT, in which case we can't say i = -i + i = i + 10; + i = -i; + *p-- = '0' + i % 10; + // Undo what we did a moment ago + i = i / 10 + 1; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + *p = '-'; + return p; + } + } +} + +// Offset into buffer where FastInt32ToBuffer places the end of string +// null character. Also used by FastInt32ToBufferLeft +static const int kFastInt32ToBufferOffset = 11; + +// Yes, this is a duplicate of FastInt64ToBuffer. But, we need this for the +// compiler to generate 32 bit arithmetic instructions. It's much faster, at +// least with 32 bit binaries. +char *FastInt32ToBuffer(int32 i, char* buffer) { + // We could collapse the positive and negative sections, but that + // would be slightly slower for positive numbers... + // 12 bytes is enough to store -2**32, -4294967296. + char* p = buffer + kFastInt32ToBufferOffset; + *p-- = '\0'; + if (i >= 0) { + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; + } else { + // On different platforms, % and / have different behaviors for + // negative numbers, so we need to jump through hoops to make sure + // we don't divide negative numbers. + if (i > -10) { + i = -i; + *p-- = '0' + i; + *p = '-'; + return p; + } else { + // Make sure we aren't at MIN_INT, in which case we can't say i = -i + i = i + 10; + i = -i; + *p-- = '0' + i % 10; + // Undo what we did a moment ago + i = i / 10 + 1; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + *p = '-'; + return p; + } + } +} + +char *FastHexToBuffer(int i, char* buffer) { + GOOGLE_CHECK(i >= 0) << "FastHexToBuffer() wants non-negative integers, not " << i; + + static const char *hexdigits = "0123456789abcdef"; + char *p = buffer + 21; + *p-- = '\0'; + do { + *p-- = hexdigits[i & 15]; // mod by 16 + i >>= 4; // divide by 16 + } while (i > 0); + return p + 1; +} + +char *InternalFastHexToBuffer(uint64 value, char* buffer, int num_byte) { + static const char *hexdigits = "0123456789abcdef"; + buffer[num_byte] = '\0'; + for (int i = num_byte - 1; i >= 0; i--) { + buffer[i] = hexdigits[uint32(value) & 0xf]; + value >>= 4; + } + return buffer; +} + +char *FastHex64ToBuffer(uint64 value, char* buffer) { + return InternalFastHexToBuffer(value, buffer, 16); +} + +char *FastHex32ToBuffer(uint32 value, char* buffer) { + return InternalFastHexToBuffer(value, buffer, 8); +} + +static inline char* PlaceNum(char* p, int num, char prev_sep) { + *p-- = '0' + num % 10; + *p-- = '0' + num / 10; + *p-- = prev_sep; + return p; +} + +// ---------------------------------------------------------------------- +// FastInt32ToBufferLeft() +// FastUInt32ToBufferLeft() +// FastInt64ToBufferLeft() +// FastUInt64ToBufferLeft() +// +// Like the Fast*ToBuffer() functions above, these are intended for speed. +// Unlike the Fast*ToBuffer() functions, however, these functions write +// their output to the beginning of the buffer (hence the name, as the +// output is left-aligned). The caller is responsible for ensuring that +// the buffer has enough space to hold the output. +// +// Returns a pointer to the end of the string (i.e. the null character +// terminating the string). +// ---------------------------------------------------------------------- + +static const char two_ASCII_digits[100][2] = { + {'0','0'}, {'0','1'}, {'0','2'}, {'0','3'}, {'0','4'}, + {'0','5'}, {'0','6'}, {'0','7'}, {'0','8'}, {'0','9'}, + {'1','0'}, {'1','1'}, {'1','2'}, {'1','3'}, {'1','4'}, + {'1','5'}, {'1','6'}, {'1','7'}, {'1','8'}, {'1','9'}, + {'2','0'}, {'2','1'}, {'2','2'}, {'2','3'}, {'2','4'}, + {'2','5'}, {'2','6'}, {'2','7'}, {'2','8'}, {'2','9'}, + {'3','0'}, {'3','1'}, {'3','2'}, {'3','3'}, {'3','4'}, + {'3','5'}, {'3','6'}, {'3','7'}, {'3','8'}, {'3','9'}, + {'4','0'}, {'4','1'}, {'4','2'}, {'4','3'}, {'4','4'}, + {'4','5'}, {'4','6'}, {'4','7'}, {'4','8'}, {'4','9'}, + {'5','0'}, {'5','1'}, {'5','2'}, {'5','3'}, {'5','4'}, + {'5','5'}, {'5','6'}, {'5','7'}, {'5','8'}, {'5','9'}, + {'6','0'}, {'6','1'}, {'6','2'}, {'6','3'}, {'6','4'}, + {'6','5'}, {'6','6'}, {'6','7'}, {'6','8'}, {'6','9'}, + {'7','0'}, {'7','1'}, {'7','2'}, {'7','3'}, {'7','4'}, + {'7','5'}, {'7','6'}, {'7','7'}, {'7','8'}, {'7','9'}, + {'8','0'}, {'8','1'}, {'8','2'}, {'8','3'}, {'8','4'}, + {'8','5'}, {'8','6'}, {'8','7'}, {'8','8'}, {'8','9'}, + {'9','0'}, {'9','1'}, {'9','2'}, {'9','3'}, {'9','4'}, + {'9','5'}, {'9','6'}, {'9','7'}, {'9','8'}, {'9','9'} +}; + +char* FastUInt32ToBufferLeft(uint32 u, char* buffer) { + int digits; + const char *ASCII_digits = NULL; + // The idea of this implementation is to trim the number of divides to as few + // as possible by using multiplication and subtraction rather than mod (%), + // and by outputting two digits at a time rather than one. + // The huge-number case is first, in the hopes that the compiler will output + // that case in one branch-free block of code, and only output conditional + // branches into it from below. + if (u >= 1000000000) { // >= 1,000,000,000 + digits = u / 100000000; // 100,000,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt100_000_000: + u -= digits * 100000000; // 100,000,000 +lt100_000_000: + digits = u / 1000000; // 1,000,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt1_000_000: + u -= digits * 1000000; // 1,000,000 +lt1_000_000: + digits = u / 10000; // 10,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt10_000: + u -= digits * 10000; // 10,000 +lt10_000: + digits = u / 100; + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt100: + u -= digits * 100; +lt100: + digits = u; + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +done: + *buffer = 0; + return buffer; + } + + if (u < 100) { + digits = u; + if (u >= 10) goto lt100; + *buffer++ = '0' + digits; + goto done; + } + if (u < 10000) { // 10,000 + if (u >= 1000) goto lt10_000; + digits = u / 100; + *buffer++ = '0' + digits; + goto sublt100; + } + if (u < 1000000) { // 1,000,000 + if (u >= 100000) goto lt1_000_000; + digits = u / 10000; // 10,000 + *buffer++ = '0' + digits; + goto sublt10_000; + } + if (u < 100000000) { // 100,000,000 + if (u >= 10000000) goto lt100_000_000; + digits = u / 1000000; // 1,000,000 + *buffer++ = '0' + digits; + goto sublt1_000_000; + } + // we already know that u < 1,000,000,000 + digits = u / 100000000; // 100,000,000 + *buffer++ = '0' + digits; + goto sublt100_000_000; +} + +char* FastInt32ToBufferLeft(int32 i, char* buffer) { + uint32 u = i; + if (i < 0) { + *buffer++ = '-'; + u = -i; + } + return FastUInt32ToBufferLeft(u, buffer); +} + +char* FastUInt64ToBufferLeft(uint64 u64, char* buffer) { + int digits; + const char *ASCII_digits = NULL; + + uint32 u = static_cast(u64); + if (u == u64) return FastUInt32ToBufferLeft(u, buffer); + + uint64 top_11_digits = u64 / 1000000000; + buffer = FastUInt64ToBufferLeft(top_11_digits, buffer); + u = u64 - (top_11_digits * 1000000000); + + digits = u / 10000000; // 10,000,000 + GOOGLE_DCHECK_LT(digits, 100); + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 10000000; // 10,000,000 + digits = u / 100000; // 100,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 100000; // 100,000 + digits = u / 1000; // 1,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 1000; // 1,000 + digits = u / 10; + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 10; + digits = u; + *buffer++ = '0' + digits; + *buffer = 0; + return buffer; +} + +char* FastInt64ToBufferLeft(int64 i, char* buffer) { + uint64 u = i; + if (i < 0) { + *buffer++ = '-'; + u = -i; + } + return FastUInt64ToBufferLeft(u, buffer); +} + +// ---------------------------------------------------------------------- +// SimpleItoa() +// Description: converts an integer to a string. +// +// Return value: string +// ---------------------------------------------------------------------- + +string SimpleItoa(int i) { + char buffer[kFastToBufferSize]; + return (sizeof(i) == 4) ? + FastInt32ToBuffer(i, buffer) : + FastInt64ToBuffer(i, buffer); +} + +string SimpleItoa(unsigned int i) { + char buffer[kFastToBufferSize]; + return string(buffer, (sizeof(i) == 4) ? + FastUInt32ToBufferLeft(i, buffer) : + FastUInt64ToBufferLeft(i, buffer)); +} + +string SimpleItoa(long i) { + char buffer[kFastToBufferSize]; + return (sizeof(i) == 4) ? + FastInt32ToBuffer(i, buffer) : + FastInt64ToBuffer(i, buffer); +} + +string SimpleItoa(unsigned long i) { + char buffer[kFastToBufferSize]; + return string(buffer, (sizeof(i) == 4) ? + FastUInt32ToBufferLeft(i, buffer) : + FastUInt64ToBufferLeft(i, buffer)); +} + +string SimpleItoa(long long i) { + char buffer[kFastToBufferSize]; + return (sizeof(i) == 4) ? + FastInt32ToBuffer(i, buffer) : + FastInt64ToBuffer(i, buffer); +} + +string SimpleItoa(unsigned long long i) { + char buffer[kFastToBufferSize]; + return string(buffer, (sizeof(i) == 4) ? + FastUInt32ToBufferLeft(i, buffer) : + FastUInt64ToBufferLeft(i, buffer)); +} + +// ---------------------------------------------------------------------- +// SimpleDtoa() +// SimpleFtoa() +// DoubleToBuffer() +// FloatToBuffer() +// We want to print the value without losing precision, but we also do +// not want to print more digits than necessary. This turns out to be +// trickier than it sounds. Numbers like 0.2 cannot be represented +// exactly in binary. If we print 0.2 with a very large precision, +// e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167". +// On the other hand, if we set the precision too low, we lose +// significant digits when printing numbers that actually need them. +// It turns out there is no precision value that does the right thing +// for all numbers. +// +// Our strategy is to first try printing with a precision that is never +// over-precise, then parse the result with strtod() to see if it +// matches. If not, we print again with a precision that will always +// give a precise result, but may use more digits than necessary. +// +// An arguably better strategy would be to use the algorithm described +// in "How to Print Floating-Point Numbers Accurately" by Steele & +// White, e.g. as implemented by David M. Gay's dtoa(). It turns out, +// however, that the following implementation is about as fast as +// DMG's code. Furthermore, DMG's code locks mutexes, which means it +// will not scale well on multi-core machines. DMG's code is slightly +// more accurate (in that it will never use more digits than +// necessary), but this is probably irrelevant for most users. +// +// Rob Pike and Ken Thompson also have an implementation of dtoa() in +// third_party/fmt/fltfmt.cc. Their implementation is similar to this +// one in that it makes guesses and then uses strtod() to check them. +// Their implementation is faster because they use their own code to +// generate the digits in the first place rather than use snprintf(), +// thus avoiding format string parsing overhead. However, this makes +// it considerably more complicated than the following implementation, +// and it is embedded in a larger library. If speed turns out to be +// an issue, we could re-implement this in terms of their +// implementation. +// ---------------------------------------------------------------------- + +string SimpleDtoa(double value) { + char buffer[kDoubleToBufferSize]; + return DoubleToBuffer(value, buffer); +} + +string SimpleFtoa(float value) { + char buffer[kFloatToBufferSize]; + return FloatToBuffer(value, buffer); +} + +static inline bool IsValidFloatChar(char c) { + return ('0' <= c && c <= '9') || + c == 'e' || c == 'E' || + c == '+' || c == '-'; +} + +void DelocalizeRadix(char* buffer) { + // Fast check: if the buffer has a normal decimal point, assume no + // translation is needed. + if (strchr(buffer, '.') != NULL) return; + + // Find the first unknown character. + while (IsValidFloatChar(*buffer)) ++buffer; + + if (*buffer == '\0') { + // No radix character found. + return; + } + + // We are now pointing at the locale-specific radix character. Replace it + // with '.'. + *buffer = '.'; + ++buffer; + + if (!IsValidFloatChar(*buffer) && *buffer != '\0') { + // It appears the radix was a multi-byte character. We need to remove the + // extra bytes. + char* target = buffer; + do { ++buffer; } while (!IsValidFloatChar(*buffer) && *buffer != '\0'); + memmove(target, buffer, strlen(buffer) + 1); + } +} + +char* DoubleToBuffer(double value, char* buffer) { + // DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all + // platforms these days. Just in case some system exists where DBL_DIG + // is significantly larger -- and risks overflowing our buffer -- we have + // this assert. + GOOGLE_COMPILE_ASSERT(DBL_DIG < 20, DBL_DIG_is_too_big); + + if (value == numeric_limits::infinity()) { + strcpy(buffer, "inf"); + return buffer; + } else if (value == -numeric_limits::infinity()) { + strcpy(buffer, "-inf"); + return buffer; + } else if (IsNaN(value)) { + strcpy(buffer, "nan"); + return buffer; + } + + int snprintf_result = + snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value); + + // The snprintf should never overflow because the buffer is significantly + // larger than the precision we asked for. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); + + // We need to make parsed_value volatile in order to force the compiler to + // write it out to the stack. Otherwise, it may keep the value in a + // register, and if it does that, it may keep it as a long double instead + // of a double. This long double may have extra bits that make it compare + // unequal to "value" even though it would be exactly equal if it were + // truncated to a double. + volatile double parsed_value = strtod(buffer, NULL); + if (parsed_value != value) { + int snprintf_result = + snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value); + + // Should never overflow; see above. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); + } + + DelocalizeRadix(buffer); + return buffer; +} + +bool safe_strtof(const char* str, float* value) { + char* endptr; + errno = 0; // errno only gets set on errors +#ifdef _WIN32 // has no strtof() + *value = strtod(str, &endptr); +#else + *value = strtof(str, &endptr); +#endif + return *str != 0 && *endptr == 0 && errno == 0; +} + +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 + // is significantly larger -- and risks overflowing our buffer -- we have + // this assert. + GOOGLE_COMPILE_ASSERT(FLT_DIG < 10, FLT_DIG_is_too_big); + + if (value == numeric_limits::infinity()) { + strcpy(buffer, "inf"); + return buffer; + } else if (value == -numeric_limits::infinity()) { + strcpy(buffer, "-inf"); + return buffer; + } else if (IsNaN(value)) { + strcpy(buffer, "nan"); + return buffer; + } + + int snprintf_result = + snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value); + + // The snprintf should never overflow because the buffer is significantly + // larger than the precision we asked for. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); + + float parsed_value; + if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) { + int snprintf_result = + snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value); + + // Should never overflow; see above. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); + } + + DelocalizeRadix(buffer); + return buffer; +} + +// ---------------------------------------------------------------------- +// NoLocaleStrtod() +// This code will make you cry. +// ---------------------------------------------------------------------- + +// Returns a string identical to *input except that the character pointed to +// by radix_pos (which should be '.') is replaced with the locale-specific +// radix character. +string LocalizeRadix(const char* input, const char* radix_pos) { + // Determine the locale-specific radix character by calling sprintf() to + // print the number 1.5, then stripping off the digits. As far as I can + // tell, this is the only portable, thread-safe way to get the C library + // to divuldge the locale's radix character. No, localeconv() is NOT + // thread-safe. + char temp[16]; + int size = sprintf(temp, "%.1f", 1.5); + GOOGLE_CHECK_EQ(temp[0], '1'); + GOOGLE_CHECK_EQ(temp[size-1], '5'); + GOOGLE_CHECK_LE(size, 6); + + // Now replace the '.' in the input with it. + string result; + result.reserve(strlen(input) + size - 3); + result.append(input, radix_pos); + result.append(temp + 1, size - 2); + result.append(radix_pos + 1); + return result; +} + +double NoLocaleStrtod(const char* text, char** original_endptr) { + // We cannot simply set the locale to "C" temporarily with setlocale() + // as this is not thread-safe. Instead, we try to parse in the current + // locale first. If parsing stops at a '.' character, then this is a + // pretty good hint that we're actually in some other locale in which + // '.' is not the radix character. + + char* temp_endptr; + double result = strtod(text, &temp_endptr); + if (original_endptr != NULL) *original_endptr = temp_endptr; + if (*temp_endptr != '.') return result; + + // Parsing halted on a '.'. Perhaps we're in a different locale? Let's + // try to replace the '.' with a locale-specific radix character and + // try again. + string localized = LocalizeRadix(text, temp_endptr); + const char* localized_cstr = localized.c_str(); + char* localized_endptr; + result = strtod(localized_cstr, &localized_endptr); + if ((localized_endptr - localized_cstr) > + (temp_endptr - text)) { + // This attempt got further, so replacing the decimal must have helped. + // Update original_endptr to point at the right location. + if (original_endptr != NULL) { + // size_diff is non-zero if the localized radix has multiple bytes. + int size_diff = localized.size() - strlen(text); + // const_cast is necessary to match the strtod() interface. + *original_endptr = const_cast( + text + (localized_endptr - localized_cstr - size_diff)); + } + } + + return result; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h new file mode 100644 index 00000000..ff919617 --- /dev/null +++ b/src/google/protobuf/stubs/strutil.h @@ -0,0 +1,432 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// from google3/strings/strutil.h + +#ifndef GOOGLE_PROTOBUF_STUBS_STRUTIL_H__ +#define GOOGLE_PROTOBUF_STUBS_STRUTIL_H__ + +#include +#include + +namespace google { +namespace protobuf { + +#ifdef _MSC_VER +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#endif + +// ---------------------------------------------------------------------- +// ascii_isalnum() +// Check if an ASCII character is alphanumeric. We can't use ctype's +// isalnum() because it is affected by locale. This function is applied +// to identifiers in the protocol buffer language, not to natural-language +// strings, so locale should not be taken into account. +// ascii_isdigit() +// Like above, but only accepts digits. +// ---------------------------------------------------------------------- + +inline bool ascii_isalnum(char c) { + return ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9'); +} + +inline bool ascii_isdigit(char c) { + return ('0' <= c && c <= '9'); +} + +// ---------------------------------------------------------------------- +// HasPrefixString() +// Check if a string begins with a given prefix. +// StripPrefixString() +// Given a string and a putative prefix, returns the string minus the +// prefix string if the prefix matches, otherwise the original +// string. +// ---------------------------------------------------------------------- +inline bool HasPrefixString(const string& str, + const string& prefix) { + return str.size() >= prefix.size() && + str.compare(0, prefix.size(), prefix) == 0; +} + +inline string StripPrefixString(const string& str, const string& prefix) { + if (HasPrefixString(str, prefix)) { + return str.substr(prefix.size()); + } else { + return str; + } +} + +// ---------------------------------------------------------------------- +// HasSuffixString() +// Return true if str ends in suffix. +// StripSuffixString() +// Given a string and a putative suffix, returns the string minus the +// suffix string if the suffix matches, otherwise the original +// string. +// ---------------------------------------------------------------------- +inline bool HasSuffixString(const string& str, + const string& suffix) { + return str.size() >= suffix.size() && + str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +} + +inline string StripSuffixString(const string& str, const string& suffix) { + if (HasSuffixString(str, suffix)) { + return str.substr(0, str.size() - suffix.size()); + } else { + return str; + } +} + +// ---------------------------------------------------------------------- +// StripString +// Replaces any occurrence of the character 'remove' (or the characters +// in 'remove') with the character 'replacewith'. +// Good for keeping html characters or protocol characters (\t) out +// of places where they might cause a problem. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT void StripString(string* s, const char* remove, + char replacewith); + +// ---------------------------------------------------------------------- +// LowerString() +// UpperString() +// Convert the characters in "s" to lowercase or uppercase. ASCII-only: +// these functions intentionally ignore locale because they are applied to +// identifiers used in the Protocol Buffer language, not to natural-language +// strings. +// ---------------------------------------------------------------------- + +inline void LowerString(string * s) { + string::iterator end = s->end(); + for (string::iterator i = s->begin(); i != end; ++i) { + // tolower() changes based on locale. We don't want this! + if ('A' <= *i && *i <= 'Z') *i += 'a' - 'A'; + } +} + +inline void UpperString(string * s) { + string::iterator end = s->end(); + for (string::iterator i = s->begin(); i != end; ++i) { + // toupper() changes based on locale. We don't want this! + if ('a' <= *i && *i <= 'z') *i += 'A' - 'a'; + } +} + +// ---------------------------------------------------------------------- +// StringReplace() +// Give me a string and two patterns "old" and "new", and I replace +// the first instance of "old" in the string with "new", if it +// exists. RETURN a new string, regardless of whether the replacement +// happened or not. +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT string StringReplace(const string& s, const string& oldsub, + const string& newsub, bool replace_all); + +// ---------------------------------------------------------------------- +// SplitStringUsing() +// Split a string using a character delimiter. Append the components +// to 'result'. If there are consecutive delimiters, this function skips +// over all of them. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT void SplitStringUsing(const string& full, const char* delim, + vector* res); + +// ---------------------------------------------------------------------- +// JoinStrings() +// These methods concatenate a vector of strings into a C++ string, using +// the C-string "delim" as a separator between components. There are two +// flavors of the function, one flavor returns the concatenated string, +// another takes a pointer to the target string. In the latter case the +// target string is cleared and overwritten. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT void JoinStrings(const vector& components, + const char* delim, string* result); + +inline string JoinStrings(const vector& components, + const char* delim) { + string result; + JoinStrings(components, delim, &result); + return result; +} + +// ---------------------------------------------------------------------- +// UnescapeCEscapeSequences() +// Copies "source" to "dest", rewriting C-style escape sequences +// -- '\n', '\r', '\\', '\ooo', etc -- to their ASCII +// equivalents. "dest" must be sufficiently large to hold all +// the characters in the rewritten string (i.e. at least as large +// as strlen(source) + 1 should be safe, since the replacements +// are always shorter than the original escaped sequences). It's +// safe for source and dest to be the same. RETURNS the length +// of dest. +// +// It allows hex sequences \xhh, or generally \xhhhhh with an +// arbitrary number of hex digits, but all of them together must +// specify a value of a single byte (e.g. \x0045 is equivalent +// to \x45, and \x1234 is erroneous). +// +// It also allows escape sequences of the form \uhhhh (exactly four +// hex digits, upper or lower case) or \Uhhhhhhhh (exactly eight +// hex digits, upper or lower case) to specify a Unicode code +// point. The dest array will contain the UTF8-encoded version of +// that code-point (e.g., if source contains \u2019, then dest will +// contain the three bytes 0xE2, 0x80, and 0x99). For the inverse +// transformation, use UniLib::UTF8EscapeString +// (util/utf8/unilib.h), not CEscapeString. +// +// Errors: In the first form of the call, errors are reported with +// LOG(ERROR). The same is true for the second form of the call if +// the pointer to the string vector is NULL; otherwise, error +// messages are stored in the vector. In either case, the effect on +// the dest array is not defined, but rest of the source will be +// processed. +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest); +LIBPROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest, + vector *errors); + +// ---------------------------------------------------------------------- +// UnescapeCEscapeString() +// This does the same thing as UnescapeCEscapeSequences, but creates +// a new string. The caller does not need to worry about allocating +// a dest buffer. This should be used for non performance critical +// tasks such as printing debug messages. It is safe for src and dest +// to be the same. +// +// The second call stores its errors in a supplied string vector. +// If the string vector pointer is NULL, it reports the errors with LOG(). +// +// In the first and second calls, the length of dest is returned. In the +// the third call, the new string is returned. +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest); +LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest, + vector *errors); +LIBPROTOBUF_EXPORT string UnescapeCEscapeString(const string& src); + +// ---------------------------------------------------------------------- +// CEscapeString() +// Copies 'src' to 'dest', escaping dangerous characters using +// C-style escape sequences. This is very useful for preparing query +// flags. 'src' and 'dest' should not overlap. +// Returns the number of bytes written to 'dest' (not including the \0) +// or -1 if there was insufficient space. +// +// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT int CEscapeString(const char* src, int src_len, + char* dest, int dest_len); + +// ---------------------------------------------------------------------- +// CEscape() +// More convenient form of CEscapeString: returns result as a "string". +// This version is slower than CEscapeString() because it does more +// allocation. However, it is much more convenient to use in +// non-speed-critical code like logging messages etc. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT string CEscape(const string& src); + +// ---------------------------------------------------------------------- +// strto32() +// strtou32() +// strto64() +// strtou64() +// Architecture-neutral plug compatible replacements for strtol() and +// strtoul(). Long's have different lengths on ILP-32 and LP-64 +// platforms, so using these is safer, from the point of view of +// overflow behavior, than using the standard libc functions. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT int32 strto32_adaptor(const char *nptr, char **endptr, + int base); +LIBPROTOBUF_EXPORT uint32 strtou32_adaptor(const char *nptr, char **endptr, + int base); + +inline int32 strto32(const char *nptr, char **endptr, int base) { + if (sizeof(int32) == sizeof(long)) + return strtol(nptr, endptr, base); + else + return strto32_adaptor(nptr, endptr, base); +} + +inline uint32 strtou32(const char *nptr, char **endptr, int base) { + if (sizeof(uint32) == sizeof(unsigned long)) + return strtoul(nptr, endptr, base); + else + return strtou32_adaptor(nptr, endptr, base); +} + +// For now, long long is 64-bit on all the platforms we care about, so these +// functions can simply pass the call to strto[u]ll. +inline int64 strto64(const char *nptr, char **endptr, int base) { + GOOGLE_COMPILE_ASSERT(sizeof(int64) == sizeof(long long), + sizeof_int64_is_not_sizeof_long_long); + return strtoll(nptr, endptr, base); +} + +inline uint64 strtou64(const char *nptr, char **endptr, int base) { + GOOGLE_COMPILE_ASSERT(sizeof(uint64) == sizeof(unsigned long long), + sizeof_uint64_is_not_sizeof_long_long); + return strtoull(nptr, endptr, base); +} + +// ---------------------------------------------------------------------- +// FastIntToBuffer() +// FastHexToBuffer() +// FastHex64ToBuffer() +// FastHex32ToBuffer() +// FastTimeToBuffer() +// These are intended for speed. FastIntToBuffer() assumes the +// integer is non-negative. FastHexToBuffer() puts output in +// hex rather than decimal. FastTimeToBuffer() puts the output +// into RFC822 format. +// +// FastHex64ToBuffer() puts a 64-bit unsigned value in hex-format, +// padded to exactly 16 bytes (plus one byte for '\0') +// +// FastHex32ToBuffer() puts a 32-bit unsigned value in hex-format, +// padded to exactly 8 bytes (plus one byte for '\0') +// +// All functions take the output buffer as an arg. +// They all return a pointer to the beginning of the output, +// which may not be the beginning of the input buffer. +// ---------------------------------------------------------------------- + +// Suggested buffer size for FastToBuffer functions. Also works with +// DoubleToBuffer() and FloatToBuffer(). +static const int kFastToBufferSize = 32; + +LIBPROTOBUF_EXPORT char* FastInt32ToBuffer(int32 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastInt64ToBuffer(int64 i, char* buffer); +char* FastUInt32ToBuffer(uint32 i, char* buffer); // inline below +char* FastUInt64ToBuffer(uint64 i, char* buffer); // inline below +LIBPROTOBUF_EXPORT char* FastHexToBuffer(int i, char* buffer); +LIBPROTOBUF_EXPORT char* FastHex64ToBuffer(uint64 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastHex32ToBuffer(uint32 i, char* buffer); + +// at least 22 bytes long +inline char* FastIntToBuffer(int i, char* buffer) { + return (sizeof(i) == 4 ? + FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer)); +} +inline char* FastUIntToBuffer(unsigned int i, char* buffer) { + return (sizeof(i) == 4 ? + FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer)); +} +inline char* FastLongToBuffer(long i, char* buffer) { + return (sizeof(i) == 4 ? + FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer)); +} +inline char* FastULongToBuffer(unsigned long i, char* buffer) { + return (sizeof(i) == 4 ? + FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer)); +} + +// ---------------------------------------------------------------------- +// FastInt32ToBufferLeft() +// FastUInt32ToBufferLeft() +// FastInt64ToBufferLeft() +// FastUInt64ToBufferLeft() +// +// Like the Fast*ToBuffer() functions above, these are intended for speed. +// Unlike the Fast*ToBuffer() functions, however, these functions write +// their output to the beginning of the buffer (hence the name, as the +// output is left-aligned). The caller is responsible for ensuring that +// the buffer has enough space to hold the output. +// +// Returns a pointer to the end of the string (i.e. the null character +// terminating the string). +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT char* FastInt32ToBufferLeft(int32 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastUInt32ToBufferLeft(uint32 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastInt64ToBufferLeft(int64 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastUInt64ToBufferLeft(uint64 i, char* buffer); + +// Just define these in terms of the above. +inline char* FastUInt32ToBuffer(uint32 i, char* buffer) { + FastUInt32ToBufferLeft(i, buffer); + return buffer; +} +inline char* FastUInt64ToBuffer(uint64 i, char* buffer) { + FastUInt64ToBufferLeft(i, buffer); + return buffer; +} + +// ---------------------------------------------------------------------- +// SimpleItoa() +// Description: converts an integer to a string. +// +// Return value: string +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT string SimpleItoa(int i); +LIBPROTOBUF_EXPORT string SimpleItoa(unsigned int i); +LIBPROTOBUF_EXPORT string SimpleItoa(long i); +LIBPROTOBUF_EXPORT string SimpleItoa(unsigned long i); +LIBPROTOBUF_EXPORT string SimpleItoa(long long i); +LIBPROTOBUF_EXPORT string SimpleItoa(unsigned long long i); + +// ---------------------------------------------------------------------- +// SimpleDtoa() +// SimpleFtoa() +// DoubleToBuffer() +// FloatToBuffer() +// Description: converts a double or float to a string which, if +// passed to NoLocaleStrtod(), will produce the exact same original double +// (except in case of NaN; all NaNs are considered the same value). +// We try to keep the string short but it's not guaranteed to be as +// short as possible. +// +// DoubleToBuffer() and FloatToBuffer() write the text to the given +// buffer and return it. The buffer must be at least +// kDoubleToBufferSize bytes for doubles and kFloatToBufferSize +// bytes for floats. kFastToBufferSize is also guaranteed to be large +// enough to hold either. +// +// Return value: string +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT string SimpleDtoa(double value); +LIBPROTOBUF_EXPORT string SimpleFtoa(float value); + +LIBPROTOBUF_EXPORT char* DoubleToBuffer(double i, char* buffer); +LIBPROTOBUF_EXPORT char* FloatToBuffer(float i, char* buffer); + +// In practice, doubles should never need more than 24 bytes and floats +// should never need more than 14 (including null terminators), but we +// overestimate to be safe. +static const int kDoubleToBufferSize = 32; +static const int kFloatToBufferSize = 24; + +// ---------------------------------------------------------------------- +// NoLocaleStrtod() +// Exactly like strtod(), except it always behaves as if in the "C" +// locale (i.e. decimal points must be '.'s). +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT double NoLocaleStrtod(const char* text, char** endptr); + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_STRUTIL_H__ + + diff --git a/src/google/protobuf/stubs/strutil_unittest.cc b/src/google/protobuf/stubs/strutil_unittest.cc new file mode 100644 index 00000000..58ffd32e --- /dev/null +++ b/src/google/protobuf/stubs/strutil_unittest.cc @@ -0,0 +1,68 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) + +#include + +#include +#include + +namespace google { +namespace protobuf { +namespace { + +// TODO(kenton): Copy strutil tests from google3? + +TEST(StringUtilityTest, ImmuneToLocales) { + // Remember the old locale. + char* old_locale_cstr = setlocale(LC_NUMERIC, NULL); + ASSERT_TRUE(old_locale_cstr != NULL); + string old_locale = old_locale_cstr; + + // Set the locale to "C". + ASSERT_TRUE(setlocale(LC_NUMERIC, "C") != NULL); + + EXPECT_EQ(1.5, NoLocaleStrtod("1.5", NULL)); + EXPECT_EQ("1.5", SimpleDtoa(1.5)); + EXPECT_EQ("1.5", SimpleFtoa(1.5)); + + // Verify that the endptr is set correctly even if not all text was parsed. + const char* text = "1.5f"; + char* endptr; + EXPECT_EQ(1.5, NoLocaleStrtod(text, &endptr)); + EXPECT_EQ(3, endptr - text); + + if (setlocale(LC_NUMERIC, "es_ES") == NULL && + setlocale(LC_NUMERIC, "es_ES.utf8") == NULL) { + // Some systems may not have the desired locale available. + GOOGLE_LOG(WARNING) + << "Couldn't set locale to es_ES. Skipping this test."; + } else { + EXPECT_EQ(1.5, NoLocaleStrtod("1.5", NULL)); + EXPECT_EQ("1.5", SimpleDtoa(1.5)); + EXPECT_EQ("1.5", SimpleFtoa(1.5)); + EXPECT_EQ(1.5, NoLocaleStrtod(text, &endptr)); + EXPECT_EQ(3, endptr - text); + } + + // Return to original locale. + setlocale(LC_NUMERIC, old_locale.c_str()); +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/substitute.cc b/src/google/protobuf/stubs/substitute.cc new file mode 100644 index 00000000..340be5e8 --- /dev/null +++ b/src/google/protobuf/stubs/substitute.cc @@ -0,0 +1,120 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace strings { + +using internal::SubstituteArg; + +// Returns the number of args in arg_array which were passed explicitly +// to Substitute(). +static int CountSubstituteArgs(const SubstituteArg* const* args_array) { + int count = 0; + while (args_array[count] != NULL && args_array[count]->size() != -1) { + ++count; + } + return count; +} + +string Substitute( + const char* format, + const SubstituteArg& arg0, const SubstituteArg& arg1, + const SubstituteArg& arg2, const SubstituteArg& arg3, + const SubstituteArg& arg4, const SubstituteArg& arg5, + const SubstituteArg& arg6, const SubstituteArg& arg7, + const SubstituteArg& arg8, const SubstituteArg& arg9) { + string result; + SubstituteAndAppend(&result, format, arg0, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, arg9); + return result; +} + +void SubstituteAndAppend( + string* output, const char* format, + const SubstituteArg& arg0, const SubstituteArg& arg1, + const SubstituteArg& arg2, const SubstituteArg& arg3, + const SubstituteArg& arg4, const SubstituteArg& arg5, + const SubstituteArg& arg6, const SubstituteArg& arg7, + const SubstituteArg& arg8, const SubstituteArg& arg9) { + const SubstituteArg* const args_array[] = { + &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, NULL + }; + + // Determine total size needed. + int size = 0; + for (int i = 0; format[i] != '\0'; i++) { + if (format[i] == '$') { + if (ascii_isdigit(format[i+1])) { + int index = format[i+1] - '0'; + if (args_array[index]->size() == -1) { + GOOGLE_LOG(DFATAL) + << "strings::Substitute format string invalid: asked for \"$" + << index << "\", but only " << CountSubstituteArgs(args_array) + << " args were given. Full format string was: \"" + << CEscape(format) << "\"."; + return; + } + size += args_array[index]->size(); + ++i; // Skip next char. + } else if (format[i+1] == '$') { + ++size; + ++i; // Skip next char. + } else { + GOOGLE_LOG(DFATAL) + << "Invalid strings::Substitute() format string: \"" + << CEscape(format) << "\"."; + return; + } + } else { + ++size; + } + } + + if (size == 0) return; + + // Build the string. + int original_size = output->size(); + STLStringResizeUninitialized(output, original_size + size); + char* target = string_as_array(output) + original_size; + for (int i = 0; format[i] != '\0'; i++) { + if (format[i] == '$') { + if (ascii_isdigit(format[i+1])) { + const SubstituteArg* src = args_array[format[i+1] - '0']; + memcpy(target, src->data(), src->size()); + target += src->size(); + ++i; // Skip next char. + } else if (format[i+1] == '$') { + *target++ = '$'; + ++i; // Skip next char. + } + } else { + *target++ = format[i]; + } + } + + GOOGLE_DCHECK_EQ(target - output->data(), output->size()); +} + +} // namespace strings +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/substitute.h b/src/google/protobuf/stubs/substitute.h new file mode 100644 index 00000000..143e4828 --- /dev/null +++ b/src/google/protobuf/stubs/substitute.h @@ -0,0 +1,156 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// from google3/strings/substitute.h + +#include +#include +#include + +#ifndef GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_ +#define GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_ + +namespace google { +namespace protobuf { +namespace strings { + +// ---------------------------------------------------------------------- +// strings::Substitute() +// strings::SubstituteAndAppend() +// Kind of like StringPrintf, but different. +// +// Example: +// string GetMessage(string first_name, string last_name, int age) { +// return strings::Substitute("My name is $0 $1 and I am $2 years old.", +// first_name, last_name, age); +// } +// +// Differences from StringPrintf: +// * The format string does not identify the types of arguments. +// Instead, the magic of C++ deals with this for us. See below +// for a list of accepted types. +// * Substitutions in the format string are identified by a '$' +// followed by a digit. So, you can use arguments out-of-order and +// use the same argument multiple times. +// * It's much faster than StringPrintf. +// +// Supported types: +// * Strings (const char*, const string&) +// * Note that this means you do not have to add .c_str() to all of +// your strings. In fact, you shouldn't; it will be slower. +// * int32, int64, uint32, uint64: Formatted using SimpleItoa(). +// * float, double: Formatted using SimpleFtoa() and SimpleDtoa(). +// * bool: Printed as "true" or "false". +// +// SubstituteAndAppend() is like Substitute() but appends the result to +// *output. Example: +// +// string str; +// strings::SubstituteAndAppend(&str, +// "My name is $0 $1 and I am $2 years old.", +// first_name, last_name, age); +// +// Substitute() is significantly faster than StringPrintf(). For very +// large strings, it may be orders of magnitude faster. +// ---------------------------------------------------------------------- + +namespace internal { // Implementation details. + +class SubstituteArg { + public: + inline SubstituteArg(const char* value) + : text_(value), size_(strlen(text_)) {} + inline SubstituteArg(const string& value) + : text_(value.data()), size_(value.size()) {} + + // Indicates that no argument was given. + inline explicit SubstituteArg() + : text_(NULL), size_(-1) {} + + // Primitives + // We don't overload for signed and unsigned char because if people are + // explicitly declaring their chars as signed or unsigned then they are + // probably actually using them as 8-bit integers and would probably + // prefer an integer representation. But, we don't really know. So, we + // make the caller decide what to do. + inline SubstituteArg(char value) + : text_(scratch_), size_(1) { scratch_[0] = value; } + inline SubstituteArg(short value) + : text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned short value) + : text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(int value) + : text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned int value) + : text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(long value) + : text_(FastLongToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned long value) + : text_(FastULongToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(long long value) + : text_(FastInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned long long value) + : text_(FastUInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(float value) + : text_(FloatToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(double value) + : text_(DoubleToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(bool value) + : text_(value ? "true" : "false"), size_(strlen(text_)) {} + + inline const char* data() const { return text_; } + inline int size() const { return size_; } + + private: + const char* text_; + int size_; + char scratch_[kFastToBufferSize]; +}; + +} // namespace internal + +LIBPROTOBUF_EXPORT string Substitute( + const char* format, + const internal::SubstituteArg& arg0 = internal::SubstituteArg(), + const internal::SubstituteArg& arg1 = internal::SubstituteArg(), + const internal::SubstituteArg& arg2 = internal::SubstituteArg(), + const internal::SubstituteArg& arg3 = internal::SubstituteArg(), + const internal::SubstituteArg& arg4 = internal::SubstituteArg(), + const internal::SubstituteArg& arg5 = internal::SubstituteArg(), + const internal::SubstituteArg& arg6 = internal::SubstituteArg(), + const internal::SubstituteArg& arg7 = internal::SubstituteArg(), + const internal::SubstituteArg& arg8 = internal::SubstituteArg(), + const internal::SubstituteArg& arg9 = internal::SubstituteArg()); + +LIBPROTOBUF_EXPORT void SubstituteAndAppend( + string* output, const char* format, + const internal::SubstituteArg& arg0 = internal::SubstituteArg(), + const internal::SubstituteArg& arg1 = internal::SubstituteArg(), + const internal::SubstituteArg& arg2 = internal::SubstituteArg(), + const internal::SubstituteArg& arg3 = internal::SubstituteArg(), + const internal::SubstituteArg& arg4 = internal::SubstituteArg(), + const internal::SubstituteArg& arg5 = internal::SubstituteArg(), + const internal::SubstituteArg& arg6 = internal::SubstituteArg(), + const internal::SubstituteArg& arg7 = internal::SubstituteArg(), + const internal::SubstituteArg& arg8 = internal::SubstituteArg(), + const internal::SubstituteArg& arg9 = internal::SubstituteArg()); + +} // namespace strings +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_ diff --git a/src/google/protobuf/test_util.cc b/src/google/protobuf/test_util.cc new file mode 100644 index 00000000..1525e94f --- /dev/null +++ b/src/google/protobuf/test_util.cc @@ -0,0 +1,1912 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +void TestUtil::SetAllFields(unittest::TestAllTypes* message) { + message->set_optional_int32 (101); + message->set_optional_int64 (102); + message->set_optional_uint32 (103); + message->set_optional_uint64 (104); + message->set_optional_sint32 (105); + message->set_optional_sint64 (106); + message->set_optional_fixed32 (107); + message->set_optional_fixed64 (108); + message->set_optional_sfixed32(109); + message->set_optional_sfixed64(110); + message->set_optional_float (111); + message->set_optional_double (112); + message->set_optional_bool (true); + message->set_optional_string ("115"); + message->set_optional_bytes ("116"); + + message->mutable_optionalgroup ()->set_a(117); + message->mutable_optional_nested_message ()->set_bb(118); + message->mutable_optional_foreign_message()->set_c(119); + message->mutable_optional_import_message ()->set_d(120); + + message->set_optional_nested_enum (unittest::TestAllTypes::BAZ); + message->set_optional_foreign_enum(unittest::FOREIGN_BAZ ); + message->set_optional_import_enum (unittest_import::IMPORT_BAZ); + + // StringPiece and Cord fields are only accessible via reflection in the + // open source release; see comments in compiler/cpp/string_field.cc. + message->GetReflection()->SetString( + message->GetDescriptor()->FindFieldByName("optional_string_piece"), + "124"); + message->GetReflection()->SetString( + message->GetDescriptor()->FindFieldByName("optional_cord"), + "125"); + + // ----------------------------------------------------------------- + + message->add_repeated_int32 (201); + message->add_repeated_int64 (202); + message->add_repeated_uint32 (203); + message->add_repeated_uint64 (204); + message->add_repeated_sint32 (205); + message->add_repeated_sint64 (206); + message->add_repeated_fixed32 (207); + message->add_repeated_fixed64 (208); + message->add_repeated_sfixed32(209); + message->add_repeated_sfixed64(210); + message->add_repeated_float (211); + message->add_repeated_double (212); + message->add_repeated_bool (true); + message->add_repeated_string ("215"); + message->add_repeated_bytes ("216"); + + message->add_repeatedgroup ()->set_a(217); + message->add_repeated_nested_message ()->set_bb(218); + message->add_repeated_foreign_message()->set_c(219); + message->add_repeated_import_message ()->set_d(220); + + message->add_repeated_nested_enum (unittest::TestAllTypes::BAR); + message->add_repeated_foreign_enum(unittest::FOREIGN_BAR ); + message->add_repeated_import_enum (unittest_import::IMPORT_BAR); + + message->GetReflection()->AddString( + message->GetDescriptor()->FindFieldByName("repeated_string_piece"), + "224"); + message->GetReflection()->AddString( + message->GetDescriptor()->FindFieldByName("repeated_cord"), + "225"); + + // Add a second one of each field. + message->add_repeated_int32 (301); + message->add_repeated_int64 (302); + message->add_repeated_uint32 (303); + message->add_repeated_uint64 (304); + message->add_repeated_sint32 (305); + message->add_repeated_sint64 (306); + message->add_repeated_fixed32 (307); + message->add_repeated_fixed64 (308); + message->add_repeated_sfixed32(309); + message->add_repeated_sfixed64(310); + message->add_repeated_float (311); + message->add_repeated_double (312); + message->add_repeated_bool (false); + message->add_repeated_string ("315"); + message->add_repeated_bytes ("316"); + + message->add_repeatedgroup ()->set_a(317); + message->add_repeated_nested_message ()->set_bb(318); + message->add_repeated_foreign_message()->set_c(319); + message->add_repeated_import_message ()->set_d(320); + + message->add_repeated_nested_enum (unittest::TestAllTypes::BAZ); + message->add_repeated_foreign_enum(unittest::FOREIGN_BAZ ); + message->add_repeated_import_enum (unittest_import::IMPORT_BAZ); + + message->GetReflection()->AddString( + message->GetDescriptor()->FindFieldByName("repeated_string_piece"), + "324"); + message->GetReflection()->AddString( + message->GetDescriptor()->FindFieldByName("repeated_cord"), + "325"); + + // ----------------------------------------------------------------- + + message->set_default_int32 (401); + message->set_default_int64 (402); + message->set_default_uint32 (403); + message->set_default_uint64 (404); + message->set_default_sint32 (405); + message->set_default_sint64 (406); + message->set_default_fixed32 (407); + message->set_default_fixed64 (408); + message->set_default_sfixed32(409); + message->set_default_sfixed64(410); + message->set_default_float (411); + message->set_default_double (412); + message->set_default_bool (false); + message->set_default_string ("415"); + message->set_default_bytes ("416"); + + message->set_default_nested_enum (unittest::TestAllTypes::FOO); + message->set_default_foreign_enum(unittest::FOREIGN_FOO ); + message->set_default_import_enum (unittest_import::IMPORT_FOO); + + message->GetReflection()->SetString( + message->GetDescriptor()->FindFieldByName("default_string_piece"), + "424"); + message->GetReflection()->SetString( + message->GetDescriptor()->FindFieldByName("default_cord"), + "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ModifyRepeatedFields(unittest::TestAllTypes* message) { + message->set_repeated_int32 (1, 501); + message->set_repeated_int64 (1, 502); + message->set_repeated_uint32 (1, 503); + message->set_repeated_uint64 (1, 504); + message->set_repeated_sint32 (1, 505); + message->set_repeated_sint64 (1, 506); + message->set_repeated_fixed32 (1, 507); + message->set_repeated_fixed64 (1, 508); + message->set_repeated_sfixed32(1, 509); + message->set_repeated_sfixed64(1, 510); + message->set_repeated_float (1, 511); + message->set_repeated_double (1, 512); + message->set_repeated_bool (1, true); + message->set_repeated_string (1, "515"); + message->set_repeated_bytes (1, "516"); + + message->mutable_repeatedgroup (1)->set_a(517); + message->mutable_repeated_nested_message (1)->set_bb(518); + message->mutable_repeated_foreign_message(1)->set_c(519); + message->mutable_repeated_import_message (1)->set_d(520); + + message->set_repeated_nested_enum (1, unittest::TestAllTypes::FOO); + message->set_repeated_foreign_enum(1, unittest::FOREIGN_FOO ); + message->set_repeated_import_enum (1, unittest_import::IMPORT_FOO); + + message->GetReflection()->SetRepeatedString( + message->GetDescriptor()->FindFieldByName("repeated_string_piece"), + 1, "424"); + message->GetReflection()->SetRepeatedString( + message->GetDescriptor()->FindFieldByName("repeated_cord"), + 1, "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectAllFieldsSet(const unittest::TestAllTypes& message) { + EXPECT_TRUE(message.has_optional_int32 ()); + EXPECT_TRUE(message.has_optional_int64 ()); + EXPECT_TRUE(message.has_optional_uint32 ()); + EXPECT_TRUE(message.has_optional_uint64 ()); + EXPECT_TRUE(message.has_optional_sint32 ()); + EXPECT_TRUE(message.has_optional_sint64 ()); + EXPECT_TRUE(message.has_optional_fixed32 ()); + EXPECT_TRUE(message.has_optional_fixed64 ()); + EXPECT_TRUE(message.has_optional_sfixed32()); + EXPECT_TRUE(message.has_optional_sfixed64()); + EXPECT_TRUE(message.has_optional_float ()); + EXPECT_TRUE(message.has_optional_double ()); + EXPECT_TRUE(message.has_optional_bool ()); + EXPECT_TRUE(message.has_optional_string ()); + EXPECT_TRUE(message.has_optional_bytes ()); + + EXPECT_TRUE(message.has_optionalgroup ()); + EXPECT_TRUE(message.has_optional_nested_message ()); + EXPECT_TRUE(message.has_optional_foreign_message()); + EXPECT_TRUE(message.has_optional_import_message ()); + + EXPECT_TRUE(message.optionalgroup ().has_a()); + EXPECT_TRUE(message.optional_nested_message ().has_bb()); + EXPECT_TRUE(message.optional_foreign_message().has_c()); + EXPECT_TRUE(message.optional_import_message ().has_d()); + + EXPECT_TRUE(message.has_optional_nested_enum ()); + EXPECT_TRUE(message.has_optional_foreign_enum()); + EXPECT_TRUE(message.has_optional_import_enum ()); + + EXPECT_TRUE(message.has_optional_string_piece()); + EXPECT_TRUE(message.has_optional_cord()); + + EXPECT_EQ(101 , message.optional_int32 ()); + EXPECT_EQ(102 , message.optional_int64 ()); + EXPECT_EQ(103 , message.optional_uint32 ()); + EXPECT_EQ(104 , message.optional_uint64 ()); + EXPECT_EQ(105 , message.optional_sint32 ()); + EXPECT_EQ(106 , message.optional_sint64 ()); + EXPECT_EQ(107 , message.optional_fixed32 ()); + EXPECT_EQ(108 , message.optional_fixed64 ()); + EXPECT_EQ(109 , message.optional_sfixed32()); + EXPECT_EQ(110 , message.optional_sfixed64()); + EXPECT_EQ(111 , message.optional_float ()); + EXPECT_EQ(112 , message.optional_double ()); + EXPECT_EQ(true , message.optional_bool ()); + EXPECT_EQ("115", message.optional_string ()); + EXPECT_EQ("116", message.optional_bytes ()); + + EXPECT_EQ(117, message.optionalgroup ().a()); + EXPECT_EQ(118, message.optional_nested_message ().bb()); + EXPECT_EQ(119, message.optional_foreign_message().c()); + EXPECT_EQ(120, message.optional_import_message ().d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.optional_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.optional_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.optional_import_enum ()); + + + // ----------------------------------------------------------------- + + ASSERT_EQ(2, message.repeated_int32_size ()); + ASSERT_EQ(2, message.repeated_int64_size ()); + ASSERT_EQ(2, message.repeated_uint32_size ()); + ASSERT_EQ(2, message.repeated_uint64_size ()); + ASSERT_EQ(2, message.repeated_sint32_size ()); + ASSERT_EQ(2, message.repeated_sint64_size ()); + ASSERT_EQ(2, message.repeated_fixed32_size ()); + ASSERT_EQ(2, message.repeated_fixed64_size ()); + ASSERT_EQ(2, message.repeated_sfixed32_size()); + ASSERT_EQ(2, message.repeated_sfixed64_size()); + ASSERT_EQ(2, message.repeated_float_size ()); + ASSERT_EQ(2, message.repeated_double_size ()); + ASSERT_EQ(2, message.repeated_bool_size ()); + ASSERT_EQ(2, message.repeated_string_size ()); + ASSERT_EQ(2, message.repeated_bytes_size ()); + + ASSERT_EQ(2, message.repeatedgroup_size ()); + ASSERT_EQ(2, message.repeated_nested_message_size ()); + ASSERT_EQ(2, message.repeated_foreign_message_size()); + ASSERT_EQ(2, message.repeated_import_message_size ()); + ASSERT_EQ(2, message.repeated_nested_enum_size ()); + ASSERT_EQ(2, message.repeated_foreign_enum_size ()); + ASSERT_EQ(2, message.repeated_import_enum_size ()); + + ASSERT_EQ(2, message.repeated_string_piece_size()); + ASSERT_EQ(2, message.repeated_cord_size()); + + EXPECT_EQ(201 , message.repeated_int32 (0)); + EXPECT_EQ(202 , message.repeated_int64 (0)); + EXPECT_EQ(203 , message.repeated_uint32 (0)); + EXPECT_EQ(204 , message.repeated_uint64 (0)); + EXPECT_EQ(205 , message.repeated_sint32 (0)); + EXPECT_EQ(206 , message.repeated_sint64 (0)); + EXPECT_EQ(207 , message.repeated_fixed32 (0)); + EXPECT_EQ(208 , message.repeated_fixed64 (0)); + EXPECT_EQ(209 , message.repeated_sfixed32(0)); + EXPECT_EQ(210 , message.repeated_sfixed64(0)); + EXPECT_EQ(211 , message.repeated_float (0)); + EXPECT_EQ(212 , message.repeated_double (0)); + EXPECT_EQ(true , message.repeated_bool (0)); + EXPECT_EQ("215", message.repeated_string (0)); + EXPECT_EQ("216", message.repeated_bytes (0)); + + EXPECT_EQ(217, message.repeatedgroup (0).a()); + EXPECT_EQ(218, message.repeated_nested_message (0).bb()); + EXPECT_EQ(219, message.repeated_foreign_message(0).c()); + EXPECT_EQ(220, message.repeated_import_message (0).d()); + + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.repeated_nested_enum (0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.repeated_foreign_enum(0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.repeated_import_enum (0)); + + EXPECT_EQ(301 , message.repeated_int32 (1)); + EXPECT_EQ(302 , message.repeated_int64 (1)); + EXPECT_EQ(303 , message.repeated_uint32 (1)); + EXPECT_EQ(304 , message.repeated_uint64 (1)); + EXPECT_EQ(305 , message.repeated_sint32 (1)); + EXPECT_EQ(306 , message.repeated_sint64 (1)); + EXPECT_EQ(307 , message.repeated_fixed32 (1)); + EXPECT_EQ(308 , message.repeated_fixed64 (1)); + EXPECT_EQ(309 , message.repeated_sfixed32(1)); + EXPECT_EQ(310 , message.repeated_sfixed64(1)); + EXPECT_EQ(311 , message.repeated_float (1)); + EXPECT_EQ(312 , message.repeated_double (1)); + EXPECT_EQ(false, message.repeated_bool (1)); + EXPECT_EQ("315", message.repeated_string (1)); + EXPECT_EQ("316", message.repeated_bytes (1)); + + EXPECT_EQ(317, message.repeatedgroup (1).a()); + EXPECT_EQ(318, message.repeated_nested_message (1).bb()); + EXPECT_EQ(319, message.repeated_foreign_message(1).c()); + EXPECT_EQ(320, message.repeated_import_message (1).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.repeated_nested_enum (1)); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.repeated_foreign_enum(1)); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.repeated_import_enum (1)); + + + // ----------------------------------------------------------------- + + EXPECT_TRUE(message.has_default_int32 ()); + EXPECT_TRUE(message.has_default_int64 ()); + EXPECT_TRUE(message.has_default_uint32 ()); + EXPECT_TRUE(message.has_default_uint64 ()); + EXPECT_TRUE(message.has_default_sint32 ()); + EXPECT_TRUE(message.has_default_sint64 ()); + EXPECT_TRUE(message.has_default_fixed32 ()); + EXPECT_TRUE(message.has_default_fixed64 ()); + EXPECT_TRUE(message.has_default_sfixed32()); + EXPECT_TRUE(message.has_default_sfixed64()); + EXPECT_TRUE(message.has_default_float ()); + EXPECT_TRUE(message.has_default_double ()); + EXPECT_TRUE(message.has_default_bool ()); + EXPECT_TRUE(message.has_default_string ()); + EXPECT_TRUE(message.has_default_bytes ()); + + EXPECT_TRUE(message.has_default_nested_enum ()); + EXPECT_TRUE(message.has_default_foreign_enum()); + EXPECT_TRUE(message.has_default_import_enum ()); + + + EXPECT_EQ(401 , message.default_int32 ()); + EXPECT_EQ(402 , message.default_int64 ()); + EXPECT_EQ(403 , message.default_uint32 ()); + EXPECT_EQ(404 , message.default_uint64 ()); + EXPECT_EQ(405 , message.default_sint32 ()); + EXPECT_EQ(406 , message.default_sint64 ()); + EXPECT_EQ(407 , message.default_fixed32 ()); + EXPECT_EQ(408 , message.default_fixed64 ()); + EXPECT_EQ(409 , message.default_sfixed32()); + EXPECT_EQ(410 , message.default_sfixed64()); + EXPECT_EQ(411 , message.default_float ()); + EXPECT_EQ(412 , message.default_double ()); + EXPECT_EQ(false, message.default_bool ()); + EXPECT_EQ("415", message.default_string ()); + EXPECT_EQ("416", message.default_bytes ()); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.default_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_FOO , message.default_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.default_import_enum ()); + +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectClear(const unittest::TestAllTypes& message) { + // has_blah() should initially be false for all optional fields. + EXPECT_FALSE(message.has_optional_int32 ()); + EXPECT_FALSE(message.has_optional_int64 ()); + EXPECT_FALSE(message.has_optional_uint32 ()); + EXPECT_FALSE(message.has_optional_uint64 ()); + EXPECT_FALSE(message.has_optional_sint32 ()); + EXPECT_FALSE(message.has_optional_sint64 ()); + EXPECT_FALSE(message.has_optional_fixed32 ()); + EXPECT_FALSE(message.has_optional_fixed64 ()); + EXPECT_FALSE(message.has_optional_sfixed32()); + EXPECT_FALSE(message.has_optional_sfixed64()); + EXPECT_FALSE(message.has_optional_float ()); + EXPECT_FALSE(message.has_optional_double ()); + EXPECT_FALSE(message.has_optional_bool ()); + EXPECT_FALSE(message.has_optional_string ()); + EXPECT_FALSE(message.has_optional_bytes ()); + + EXPECT_FALSE(message.has_optionalgroup ()); + EXPECT_FALSE(message.has_optional_nested_message ()); + EXPECT_FALSE(message.has_optional_foreign_message()); + EXPECT_FALSE(message.has_optional_import_message ()); + + EXPECT_FALSE(message.has_optional_nested_enum ()); + EXPECT_FALSE(message.has_optional_foreign_enum()); + EXPECT_FALSE(message.has_optional_import_enum ()); + + EXPECT_FALSE(message.has_optional_string_piece()); + EXPECT_FALSE(message.has_optional_cord()); + + // Optional fields without defaults are set to zero or something like it. + EXPECT_EQ(0 , message.optional_int32 ()); + EXPECT_EQ(0 , message.optional_int64 ()); + EXPECT_EQ(0 , message.optional_uint32 ()); + EXPECT_EQ(0 , message.optional_uint64 ()); + EXPECT_EQ(0 , message.optional_sint32 ()); + EXPECT_EQ(0 , message.optional_sint64 ()); + EXPECT_EQ(0 , message.optional_fixed32 ()); + EXPECT_EQ(0 , message.optional_fixed64 ()); + EXPECT_EQ(0 , message.optional_sfixed32()); + EXPECT_EQ(0 , message.optional_sfixed64()); + EXPECT_EQ(0 , message.optional_float ()); + EXPECT_EQ(0 , message.optional_double ()); + EXPECT_EQ(false, message.optional_bool ()); + EXPECT_EQ("" , message.optional_string ()); + EXPECT_EQ("" , message.optional_bytes ()); + + // Embedded messages should also be clear. + EXPECT_FALSE(message.optionalgroup ().has_a()); + EXPECT_FALSE(message.optional_nested_message ().has_bb()); + EXPECT_FALSE(message.optional_foreign_message().has_c()); + EXPECT_FALSE(message.optional_import_message ().has_d()); + + EXPECT_EQ(0, message.optionalgroup ().a()); + EXPECT_EQ(0, message.optional_nested_message ().bb()); + EXPECT_EQ(0, message.optional_foreign_message().c()); + EXPECT_EQ(0, message.optional_import_message ().d()); + + // Enums without defaults are set to the first value in the enum. + EXPECT_EQ(unittest::TestAllTypes::FOO, message.optional_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_FOO , message.optional_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.optional_import_enum ()); + + + // Repeated fields are empty. + EXPECT_EQ(0, message.repeated_int32_size ()); + EXPECT_EQ(0, message.repeated_int64_size ()); + EXPECT_EQ(0, message.repeated_uint32_size ()); + EXPECT_EQ(0, message.repeated_uint64_size ()); + EXPECT_EQ(0, message.repeated_sint32_size ()); + EXPECT_EQ(0, message.repeated_sint64_size ()); + EXPECT_EQ(0, message.repeated_fixed32_size ()); + EXPECT_EQ(0, message.repeated_fixed64_size ()); + EXPECT_EQ(0, message.repeated_sfixed32_size()); + EXPECT_EQ(0, message.repeated_sfixed64_size()); + EXPECT_EQ(0, message.repeated_float_size ()); + EXPECT_EQ(0, message.repeated_double_size ()); + EXPECT_EQ(0, message.repeated_bool_size ()); + EXPECT_EQ(0, message.repeated_string_size ()); + EXPECT_EQ(0, message.repeated_bytes_size ()); + + EXPECT_EQ(0, message.repeatedgroup_size ()); + EXPECT_EQ(0, message.repeated_nested_message_size ()); + EXPECT_EQ(0, message.repeated_foreign_message_size()); + EXPECT_EQ(0, message.repeated_import_message_size ()); + EXPECT_EQ(0, message.repeated_nested_enum_size ()); + EXPECT_EQ(0, message.repeated_foreign_enum_size ()); + EXPECT_EQ(0, message.repeated_import_enum_size ()); + + EXPECT_EQ(0, message.repeated_string_piece_size()); + EXPECT_EQ(0, message.repeated_cord_size()); + + // has_blah() should also be false for all default fields. + EXPECT_FALSE(message.has_default_int32 ()); + EXPECT_FALSE(message.has_default_int64 ()); + EXPECT_FALSE(message.has_default_uint32 ()); + EXPECT_FALSE(message.has_default_uint64 ()); + EXPECT_FALSE(message.has_default_sint32 ()); + EXPECT_FALSE(message.has_default_sint64 ()); + EXPECT_FALSE(message.has_default_fixed32 ()); + EXPECT_FALSE(message.has_default_fixed64 ()); + EXPECT_FALSE(message.has_default_sfixed32()); + EXPECT_FALSE(message.has_default_sfixed64()); + EXPECT_FALSE(message.has_default_float ()); + EXPECT_FALSE(message.has_default_double ()); + EXPECT_FALSE(message.has_default_bool ()); + EXPECT_FALSE(message.has_default_string ()); + EXPECT_FALSE(message.has_default_bytes ()); + + EXPECT_FALSE(message.has_default_nested_enum ()); + EXPECT_FALSE(message.has_default_foreign_enum()); + EXPECT_FALSE(message.has_default_import_enum ()); + + + // Fields with defaults have their default values (duh). + EXPECT_EQ( 41 , message.default_int32 ()); + EXPECT_EQ( 42 , message.default_int64 ()); + EXPECT_EQ( 43 , message.default_uint32 ()); + EXPECT_EQ( 44 , message.default_uint64 ()); + EXPECT_EQ(-45 , message.default_sint32 ()); + EXPECT_EQ( 46 , message.default_sint64 ()); + EXPECT_EQ( 47 , message.default_fixed32 ()); + EXPECT_EQ( 48 , message.default_fixed64 ()); + EXPECT_EQ( 49 , message.default_sfixed32()); + EXPECT_EQ(-50 , message.default_sfixed64()); + EXPECT_EQ( 51.5 , message.default_float ()); + EXPECT_EQ( 52e3 , message.default_double ()); + EXPECT_EQ(true , message.default_bool ()); + EXPECT_EQ("hello", message.default_string ()); + EXPECT_EQ("world", message.default_bytes ()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.default_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_BAR , message.default_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.default_import_enum ()); + +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectRepeatedFieldsModified( + const unittest::TestAllTypes& message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + ASSERT_EQ(2, message.repeated_int32_size ()); + ASSERT_EQ(2, message.repeated_int64_size ()); + ASSERT_EQ(2, message.repeated_uint32_size ()); + ASSERT_EQ(2, message.repeated_uint64_size ()); + ASSERT_EQ(2, message.repeated_sint32_size ()); + ASSERT_EQ(2, message.repeated_sint64_size ()); + ASSERT_EQ(2, message.repeated_fixed32_size ()); + ASSERT_EQ(2, message.repeated_fixed64_size ()); + ASSERT_EQ(2, message.repeated_sfixed32_size()); + ASSERT_EQ(2, message.repeated_sfixed64_size()); + ASSERT_EQ(2, message.repeated_float_size ()); + ASSERT_EQ(2, message.repeated_double_size ()); + ASSERT_EQ(2, message.repeated_bool_size ()); + ASSERT_EQ(2, message.repeated_string_size ()); + ASSERT_EQ(2, message.repeated_bytes_size ()); + + ASSERT_EQ(2, message.repeatedgroup_size ()); + ASSERT_EQ(2, message.repeated_nested_message_size ()); + ASSERT_EQ(2, message.repeated_foreign_message_size()); + ASSERT_EQ(2, message.repeated_import_message_size ()); + ASSERT_EQ(2, message.repeated_nested_enum_size ()); + ASSERT_EQ(2, message.repeated_foreign_enum_size ()); + ASSERT_EQ(2, message.repeated_import_enum_size ()); + + ASSERT_EQ(2, message.repeated_string_piece_size()); + ASSERT_EQ(2, message.repeated_cord_size()); + + EXPECT_EQ(201 , message.repeated_int32 (0)); + EXPECT_EQ(202 , message.repeated_int64 (0)); + EXPECT_EQ(203 , message.repeated_uint32 (0)); + EXPECT_EQ(204 , message.repeated_uint64 (0)); + EXPECT_EQ(205 , message.repeated_sint32 (0)); + EXPECT_EQ(206 , message.repeated_sint64 (0)); + EXPECT_EQ(207 , message.repeated_fixed32 (0)); + EXPECT_EQ(208 , message.repeated_fixed64 (0)); + EXPECT_EQ(209 , message.repeated_sfixed32(0)); + EXPECT_EQ(210 , message.repeated_sfixed64(0)); + EXPECT_EQ(211 , message.repeated_float (0)); + EXPECT_EQ(212 , message.repeated_double (0)); + EXPECT_EQ(true , message.repeated_bool (0)); + EXPECT_EQ("215", message.repeated_string (0)); + EXPECT_EQ("216", message.repeated_bytes (0)); + + EXPECT_EQ(217, message.repeatedgroup (0).a()); + EXPECT_EQ(218, message.repeated_nested_message (0).bb()); + EXPECT_EQ(219, message.repeated_foreign_message(0).c()); + EXPECT_EQ(220, message.repeated_import_message (0).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.repeated_nested_enum (0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.repeated_foreign_enum(0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.repeated_import_enum (0)); + + + // Actually verify the second (modified) elements now. + EXPECT_EQ(501 , message.repeated_int32 (1)); + EXPECT_EQ(502 , message.repeated_int64 (1)); + EXPECT_EQ(503 , message.repeated_uint32 (1)); + EXPECT_EQ(504 , message.repeated_uint64 (1)); + EXPECT_EQ(505 , message.repeated_sint32 (1)); + EXPECT_EQ(506 , message.repeated_sint64 (1)); + EXPECT_EQ(507 , message.repeated_fixed32 (1)); + EXPECT_EQ(508 , message.repeated_fixed64 (1)); + EXPECT_EQ(509 , message.repeated_sfixed32(1)); + EXPECT_EQ(510 , message.repeated_sfixed64(1)); + EXPECT_EQ(511 , message.repeated_float (1)); + EXPECT_EQ(512 , message.repeated_double (1)); + EXPECT_EQ(true , message.repeated_bool (1)); + EXPECT_EQ("515", message.repeated_string (1)); + EXPECT_EQ("516", message.repeated_bytes (1)); + + EXPECT_EQ(517, message.repeatedgroup (1).a()); + EXPECT_EQ(518, message.repeated_nested_message (1).bb()); + EXPECT_EQ(519, message.repeated_foreign_message(1).c()); + EXPECT_EQ(520, message.repeated_import_message (1).d()); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.repeated_nested_enum (1)); + EXPECT_EQ(unittest::FOREIGN_FOO , message.repeated_foreign_enum(1)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.repeated_import_enum (1)); + +} + +// =================================================================== +// Extensions +// +// All this code is exactly equivalent to the above code except that it's +// manipulating extension fields instead of normal ones. +// +// I gave up on the 80-char limit here. Sorry. + +void TestUtil::SetAllExtensions(unittest::TestAllExtensions* message) { + message->SetExtension(unittest::optional_int32_extension , 101); + message->SetExtension(unittest::optional_int64_extension , 102); + message->SetExtension(unittest::optional_uint32_extension , 103); + message->SetExtension(unittest::optional_uint64_extension , 104); + message->SetExtension(unittest::optional_sint32_extension , 105); + message->SetExtension(unittest::optional_sint64_extension , 106); + message->SetExtension(unittest::optional_fixed32_extension , 107); + message->SetExtension(unittest::optional_fixed64_extension , 108); + message->SetExtension(unittest::optional_sfixed32_extension, 109); + message->SetExtension(unittest::optional_sfixed64_extension, 110); + message->SetExtension(unittest::optional_float_extension , 111); + message->SetExtension(unittest::optional_double_extension , 112); + message->SetExtension(unittest::optional_bool_extension , true); + message->SetExtension(unittest::optional_string_extension , "115"); + message->SetExtension(unittest::optional_bytes_extension , "116"); + + message->MutableExtension(unittest::optionalgroup_extension )->set_a(117); + message->MutableExtension(unittest::optional_nested_message_extension )->set_bb(118); + message->MutableExtension(unittest::optional_foreign_message_extension)->set_c(119); + message->MutableExtension(unittest::optional_import_message_extension )->set_d(120); + + message->SetExtension(unittest::optional_nested_enum_extension , unittest::TestAllTypes::BAZ); + message->SetExtension(unittest::optional_foreign_enum_extension, unittest::FOREIGN_BAZ ); + message->SetExtension(unittest::optional_import_enum_extension , unittest_import::IMPORT_BAZ); + + message->SetExtension(unittest::optional_string_piece_extension, "124"); + message->SetExtension(unittest::optional_cord_extension, "125"); + + // ----------------------------------------------------------------- + + message->AddExtension(unittest::repeated_int32_extension , 201); + message->AddExtension(unittest::repeated_int64_extension , 202); + message->AddExtension(unittest::repeated_uint32_extension , 203); + message->AddExtension(unittest::repeated_uint64_extension , 204); + message->AddExtension(unittest::repeated_sint32_extension , 205); + message->AddExtension(unittest::repeated_sint64_extension , 206); + message->AddExtension(unittest::repeated_fixed32_extension , 207); + message->AddExtension(unittest::repeated_fixed64_extension , 208); + message->AddExtension(unittest::repeated_sfixed32_extension, 209); + message->AddExtension(unittest::repeated_sfixed64_extension, 210); + message->AddExtension(unittest::repeated_float_extension , 211); + message->AddExtension(unittest::repeated_double_extension , 212); + message->AddExtension(unittest::repeated_bool_extension , true); + message->AddExtension(unittest::repeated_string_extension , "215"); + message->AddExtension(unittest::repeated_bytes_extension , "216"); + + message->AddExtension(unittest::repeatedgroup_extension )->set_a(217); + message->AddExtension(unittest::repeated_nested_message_extension )->set_bb(218); + message->AddExtension(unittest::repeated_foreign_message_extension)->set_c(219); + message->AddExtension(unittest::repeated_import_message_extension )->set_d(220); + + message->AddExtension(unittest::repeated_nested_enum_extension , unittest::TestAllTypes::BAR); + message->AddExtension(unittest::repeated_foreign_enum_extension, unittest::FOREIGN_BAR ); + message->AddExtension(unittest::repeated_import_enum_extension , unittest_import::IMPORT_BAR); + + message->AddExtension(unittest::repeated_string_piece_extension, "224"); + message->AddExtension(unittest::repeated_cord_extension, "225"); + + // Add a second one of each field. + message->AddExtension(unittest::repeated_int32_extension , 301); + message->AddExtension(unittest::repeated_int64_extension , 302); + message->AddExtension(unittest::repeated_uint32_extension , 303); + message->AddExtension(unittest::repeated_uint64_extension , 304); + message->AddExtension(unittest::repeated_sint32_extension , 305); + message->AddExtension(unittest::repeated_sint64_extension , 306); + message->AddExtension(unittest::repeated_fixed32_extension , 307); + message->AddExtension(unittest::repeated_fixed64_extension , 308); + message->AddExtension(unittest::repeated_sfixed32_extension, 309); + message->AddExtension(unittest::repeated_sfixed64_extension, 310); + message->AddExtension(unittest::repeated_float_extension , 311); + message->AddExtension(unittest::repeated_double_extension , 312); + message->AddExtension(unittest::repeated_bool_extension , false); + message->AddExtension(unittest::repeated_string_extension , "315"); + message->AddExtension(unittest::repeated_bytes_extension , "316"); + + message->AddExtension(unittest::repeatedgroup_extension )->set_a(317); + message->AddExtension(unittest::repeated_nested_message_extension )->set_bb(318); + message->AddExtension(unittest::repeated_foreign_message_extension)->set_c(319); + message->AddExtension(unittest::repeated_import_message_extension )->set_d(320); + + message->AddExtension(unittest::repeated_nested_enum_extension , unittest::TestAllTypes::BAZ); + message->AddExtension(unittest::repeated_foreign_enum_extension, unittest::FOREIGN_BAZ ); + message->AddExtension(unittest::repeated_import_enum_extension , unittest_import::IMPORT_BAZ); + + message->AddExtension(unittest::repeated_string_piece_extension, "324"); + message->AddExtension(unittest::repeated_cord_extension, "325"); + + // ----------------------------------------------------------------- + + message->SetExtension(unittest::default_int32_extension , 401); + message->SetExtension(unittest::default_int64_extension , 402); + message->SetExtension(unittest::default_uint32_extension , 403); + message->SetExtension(unittest::default_uint64_extension , 404); + message->SetExtension(unittest::default_sint32_extension , 405); + message->SetExtension(unittest::default_sint64_extension , 406); + message->SetExtension(unittest::default_fixed32_extension , 407); + message->SetExtension(unittest::default_fixed64_extension , 408); + message->SetExtension(unittest::default_sfixed32_extension, 409); + message->SetExtension(unittest::default_sfixed64_extension, 410); + message->SetExtension(unittest::default_float_extension , 411); + message->SetExtension(unittest::default_double_extension , 412); + message->SetExtension(unittest::default_bool_extension , false); + message->SetExtension(unittest::default_string_extension , "415"); + message->SetExtension(unittest::default_bytes_extension , "416"); + + message->SetExtension(unittest::default_nested_enum_extension , unittest::TestAllTypes::FOO); + message->SetExtension(unittest::default_foreign_enum_extension, unittest::FOREIGN_FOO ); + message->SetExtension(unittest::default_import_enum_extension , unittest_import::IMPORT_FOO); + + message->SetExtension(unittest::default_string_piece_extension, "424"); + message->SetExtension(unittest::default_cord_extension, "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::SetAllFieldsAndExtensions( + unittest::TestFieldOrderings* message) { + GOOGLE_CHECK(message); + message->set_my_int(1); + message->set_my_string("foo"); + message->set_my_float(1.0); + message->SetExtension(unittest::my_extension_int, 23); + message->SetExtension(unittest::my_extension_string, "bar"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ModifyRepeatedExtensions(unittest::TestAllExtensions* message) { + message->SetExtension(unittest::repeated_int32_extension , 1, 501); + message->SetExtension(unittest::repeated_int64_extension , 1, 502); + message->SetExtension(unittest::repeated_uint32_extension , 1, 503); + message->SetExtension(unittest::repeated_uint64_extension , 1, 504); + message->SetExtension(unittest::repeated_sint32_extension , 1, 505); + message->SetExtension(unittest::repeated_sint64_extension , 1, 506); + message->SetExtension(unittest::repeated_fixed32_extension , 1, 507); + message->SetExtension(unittest::repeated_fixed64_extension , 1, 508); + message->SetExtension(unittest::repeated_sfixed32_extension, 1, 509); + message->SetExtension(unittest::repeated_sfixed64_extension, 1, 510); + message->SetExtension(unittest::repeated_float_extension , 1, 511); + message->SetExtension(unittest::repeated_double_extension , 1, 512); + message->SetExtension(unittest::repeated_bool_extension , 1, true); + message->SetExtension(unittest::repeated_string_extension , 1, "515"); + message->SetExtension(unittest::repeated_bytes_extension , 1, "516"); + + message->MutableExtension(unittest::repeatedgroup_extension , 1)->set_a(517); + message->MutableExtension(unittest::repeated_nested_message_extension , 1)->set_bb(518); + message->MutableExtension(unittest::repeated_foreign_message_extension, 1)->set_c(519); + message->MutableExtension(unittest::repeated_import_message_extension , 1)->set_d(520); + + message->SetExtension(unittest::repeated_nested_enum_extension , 1, unittest::TestAllTypes::FOO); + message->SetExtension(unittest::repeated_foreign_enum_extension, 1, unittest::FOREIGN_FOO ); + message->SetExtension(unittest::repeated_import_enum_extension , 1, unittest_import::IMPORT_FOO); + + message->SetExtension(unittest::repeated_string_piece_extension, 1, "524"); + message->SetExtension(unittest::repeated_cord_extension, 1, "525"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectAllExtensionsSet( + const unittest::TestAllExtensions& message) { + EXPECT_TRUE(message.HasExtension(unittest::optional_int32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_int64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_uint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_uint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_sint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_sint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_fixed32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_fixed64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_sfixed32_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_sfixed64_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_float_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_double_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_bool_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_string_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_bytes_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::optionalgroup_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_nested_message_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_foreign_message_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_import_message_extension )); + + EXPECT_TRUE(message.GetExtension(unittest::optionalgroup_extension ).has_a()); + EXPECT_TRUE(message.GetExtension(unittest::optional_nested_message_extension ).has_bb()); + EXPECT_TRUE(message.GetExtension(unittest::optional_foreign_message_extension).has_c()); + EXPECT_TRUE(message.GetExtension(unittest::optional_import_message_extension ).has_d()); + + EXPECT_TRUE(message.HasExtension(unittest::optional_nested_enum_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_foreign_enum_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_import_enum_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::optional_string_piece_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_cord_extension)); + + EXPECT_EQ(101 , message.GetExtension(unittest::optional_int32_extension )); + EXPECT_EQ(102 , message.GetExtension(unittest::optional_int64_extension )); + EXPECT_EQ(103 , message.GetExtension(unittest::optional_uint32_extension )); + EXPECT_EQ(104 , message.GetExtension(unittest::optional_uint64_extension )); + EXPECT_EQ(105 , message.GetExtension(unittest::optional_sint32_extension )); + EXPECT_EQ(106 , message.GetExtension(unittest::optional_sint64_extension )); + EXPECT_EQ(107 , message.GetExtension(unittest::optional_fixed32_extension )); + EXPECT_EQ(108 , message.GetExtension(unittest::optional_fixed64_extension )); + EXPECT_EQ(109 , message.GetExtension(unittest::optional_sfixed32_extension)); + EXPECT_EQ(110 , message.GetExtension(unittest::optional_sfixed64_extension)); + EXPECT_EQ(111 , message.GetExtension(unittest::optional_float_extension )); + EXPECT_EQ(112 , message.GetExtension(unittest::optional_double_extension )); + EXPECT_EQ(true , message.GetExtension(unittest::optional_bool_extension )); + EXPECT_EQ("115", message.GetExtension(unittest::optional_string_extension )); + EXPECT_EQ("116", message.GetExtension(unittest::optional_bytes_extension )); + + EXPECT_EQ(117, message.GetExtension(unittest::optionalgroup_extension ).a()); + EXPECT_EQ(118, message.GetExtension(unittest::optional_nested_message_extension ).bb()); + EXPECT_EQ(119, message.GetExtension(unittest::optional_foreign_message_extension).c()); + EXPECT_EQ(120, message.GetExtension(unittest::optional_import_message_extension ).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.GetExtension(unittest::optional_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.GetExtension(unittest::optional_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.GetExtension(unittest::optional_import_enum_extension )); + + EXPECT_EQ("124", message.GetExtension(unittest::optional_string_piece_extension)); + EXPECT_EQ("125", message.GetExtension(unittest::optional_cord_extension)); + + // ----------------------------------------------------------------- + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed32_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed64_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_float_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_double_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bool_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bytes_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeatedgroup_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_message_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_enum_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_piece_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_cord_extension)); + + EXPECT_EQ(201 , message.GetExtension(unittest::repeated_int32_extension , 0)); + EXPECT_EQ(202 , message.GetExtension(unittest::repeated_int64_extension , 0)); + EXPECT_EQ(203 , message.GetExtension(unittest::repeated_uint32_extension , 0)); + EXPECT_EQ(204 , message.GetExtension(unittest::repeated_uint64_extension , 0)); + EXPECT_EQ(205 , message.GetExtension(unittest::repeated_sint32_extension , 0)); + EXPECT_EQ(206 , message.GetExtension(unittest::repeated_sint64_extension , 0)); + EXPECT_EQ(207 , message.GetExtension(unittest::repeated_fixed32_extension , 0)); + EXPECT_EQ(208 , message.GetExtension(unittest::repeated_fixed64_extension , 0)); + EXPECT_EQ(209 , message.GetExtension(unittest::repeated_sfixed32_extension, 0)); + EXPECT_EQ(210 , message.GetExtension(unittest::repeated_sfixed64_extension, 0)); + EXPECT_EQ(211 , message.GetExtension(unittest::repeated_float_extension , 0)); + EXPECT_EQ(212 , message.GetExtension(unittest::repeated_double_extension , 0)); + EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension , 0)); + EXPECT_EQ("215", message.GetExtension(unittest::repeated_string_extension , 0)); + EXPECT_EQ("216", message.GetExtension(unittest::repeated_bytes_extension , 0)); + + EXPECT_EQ(217, message.GetExtension(unittest::repeatedgroup_extension , 0).a()); + EXPECT_EQ(218, message.GetExtension(unittest::repeated_nested_message_extension , 0).bb()); + EXPECT_EQ(219, message.GetExtension(unittest::repeated_foreign_message_extension, 0).c()); + EXPECT_EQ(220, message.GetExtension(unittest::repeated_import_message_extension , 0).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::repeated_nested_enum_extension , 0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.GetExtension(unittest::repeated_foreign_enum_extension, 0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::repeated_import_enum_extension , 0)); + + EXPECT_EQ("224", message.GetExtension(unittest::repeated_string_piece_extension, 0)); + EXPECT_EQ("225", message.GetExtension(unittest::repeated_cord_extension, 0)); + + EXPECT_EQ(301 , message.GetExtension(unittest::repeated_int32_extension , 1)); + EXPECT_EQ(302 , message.GetExtension(unittest::repeated_int64_extension , 1)); + EXPECT_EQ(303 , message.GetExtension(unittest::repeated_uint32_extension , 1)); + EXPECT_EQ(304 , message.GetExtension(unittest::repeated_uint64_extension , 1)); + EXPECT_EQ(305 , message.GetExtension(unittest::repeated_sint32_extension , 1)); + EXPECT_EQ(306 , message.GetExtension(unittest::repeated_sint64_extension , 1)); + EXPECT_EQ(307 , message.GetExtension(unittest::repeated_fixed32_extension , 1)); + EXPECT_EQ(308 , message.GetExtension(unittest::repeated_fixed64_extension , 1)); + EXPECT_EQ(309 , message.GetExtension(unittest::repeated_sfixed32_extension, 1)); + EXPECT_EQ(310 , message.GetExtension(unittest::repeated_sfixed64_extension, 1)); + EXPECT_EQ(311 , message.GetExtension(unittest::repeated_float_extension , 1)); + EXPECT_EQ(312 , message.GetExtension(unittest::repeated_double_extension , 1)); + EXPECT_EQ(false, message.GetExtension(unittest::repeated_bool_extension , 1)); + EXPECT_EQ("315", message.GetExtension(unittest::repeated_string_extension , 1)); + EXPECT_EQ("316", message.GetExtension(unittest::repeated_bytes_extension , 1)); + + EXPECT_EQ(317, message.GetExtension(unittest::repeatedgroup_extension , 1).a()); + EXPECT_EQ(318, message.GetExtension(unittest::repeated_nested_message_extension , 1).bb()); + EXPECT_EQ(319, message.GetExtension(unittest::repeated_foreign_message_extension, 1).c()); + EXPECT_EQ(320, message.GetExtension(unittest::repeated_import_message_extension , 1).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.GetExtension(unittest::repeated_nested_enum_extension , 1)); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.GetExtension(unittest::repeated_foreign_enum_extension, 1)); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.GetExtension(unittest::repeated_import_enum_extension , 1)); + + EXPECT_EQ("324", message.GetExtension(unittest::repeated_string_piece_extension, 1)); + EXPECT_EQ("325", message.GetExtension(unittest::repeated_cord_extension, 1)); + + // ----------------------------------------------------------------- + + EXPECT_TRUE(message.HasExtension(unittest::default_int32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_int64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_uint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_uint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_sint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_sint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_fixed32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_fixed64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_sfixed32_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_sfixed64_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_float_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_double_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_bool_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_string_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_bytes_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::default_nested_enum_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_foreign_enum_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_import_enum_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::default_string_piece_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_cord_extension)); + + EXPECT_EQ(401 , message.GetExtension(unittest::default_int32_extension )); + EXPECT_EQ(402 , message.GetExtension(unittest::default_int64_extension )); + EXPECT_EQ(403 , message.GetExtension(unittest::default_uint32_extension )); + EXPECT_EQ(404 , message.GetExtension(unittest::default_uint64_extension )); + EXPECT_EQ(405 , message.GetExtension(unittest::default_sint32_extension )); + EXPECT_EQ(406 , message.GetExtension(unittest::default_sint64_extension )); + EXPECT_EQ(407 , message.GetExtension(unittest::default_fixed32_extension )); + EXPECT_EQ(408 , message.GetExtension(unittest::default_fixed64_extension )); + EXPECT_EQ(409 , message.GetExtension(unittest::default_sfixed32_extension)); + EXPECT_EQ(410 , message.GetExtension(unittest::default_sfixed64_extension)); + EXPECT_EQ(411 , message.GetExtension(unittest::default_float_extension )); + EXPECT_EQ(412 , message.GetExtension(unittest::default_double_extension )); + EXPECT_EQ(false, message.GetExtension(unittest::default_bool_extension )); + EXPECT_EQ("415", message.GetExtension(unittest::default_string_extension )); + EXPECT_EQ("416", message.GetExtension(unittest::default_bytes_extension )); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.GetExtension(unittest::default_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_FOO , message.GetExtension(unittest::default_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.GetExtension(unittest::default_import_enum_extension )); + + EXPECT_EQ("424", message.GetExtension(unittest::default_string_piece_extension)); + EXPECT_EQ("425", message.GetExtension(unittest::default_cord_extension)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectExtensionsClear( + const unittest::TestAllExtensions& message) { + string serialized; + ASSERT_TRUE(message.SerializeToString(&serialized)); + EXPECT_EQ("", serialized); + EXPECT_EQ(0, message.ByteSize()); + + // has_blah() should initially be false for all optional fields. + EXPECT_FALSE(message.HasExtension(unittest::optional_int32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_int64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_uint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_uint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_sint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_sint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_fixed32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_fixed64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_sfixed32_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_sfixed64_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_float_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_double_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_bool_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_string_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_bytes_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::optionalgroup_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_nested_message_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_foreign_message_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_import_message_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::optional_nested_enum_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_foreign_enum_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_import_enum_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::optional_string_piece_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_cord_extension)); + + // Optional fields without defaults are set to zero or something like it. + EXPECT_EQ(0 , message.GetExtension(unittest::optional_int32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_int64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_uint32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_uint64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sint32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sint64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_fixed32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_fixed64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sfixed32_extension)); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sfixed64_extension)); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_float_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_double_extension )); + EXPECT_EQ(false, message.GetExtension(unittest::optional_bool_extension )); + EXPECT_EQ("" , message.GetExtension(unittest::optional_string_extension )); + EXPECT_EQ("" , message.GetExtension(unittest::optional_bytes_extension )); + + // Embedded messages should also be clear. + EXPECT_FALSE(message.GetExtension(unittest::optionalgroup_extension ).has_a()); + EXPECT_FALSE(message.GetExtension(unittest::optional_nested_message_extension ).has_bb()); + EXPECT_FALSE(message.GetExtension(unittest::optional_foreign_message_extension).has_c()); + EXPECT_FALSE(message.GetExtension(unittest::optional_import_message_extension ).has_d()); + + EXPECT_EQ(0, message.GetExtension(unittest::optionalgroup_extension ).a()); + EXPECT_EQ(0, message.GetExtension(unittest::optional_nested_message_extension ).bb()); + EXPECT_EQ(0, message.GetExtension(unittest::optional_foreign_message_extension).c()); + EXPECT_EQ(0, message.GetExtension(unittest::optional_import_message_extension ).d()); + + // Enums without defaults are set to the first value in the enum. + EXPECT_EQ(unittest::TestAllTypes::FOO, message.GetExtension(unittest::optional_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_FOO , message.GetExtension(unittest::optional_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.GetExtension(unittest::optional_import_enum_extension )); + + EXPECT_EQ("", message.GetExtension(unittest::optional_string_piece_extension)); + EXPECT_EQ("", message.GetExtension(unittest::optional_cord_extension)); + + // Repeated fields are empty. + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_int32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_int64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_uint32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_uint64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sint32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sint64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_fixed32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_fixed64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sfixed32_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sfixed64_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_float_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_double_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_bool_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_string_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_bytes_extension )); + + EXPECT_EQ(0, message.ExtensionSize(unittest::repeatedgroup_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_nested_message_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_foreign_message_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_import_message_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_nested_enum_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_foreign_enum_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_import_enum_extension )); + + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_string_piece_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_cord_extension)); + + // has_blah() should also be false for all default fields. + EXPECT_FALSE(message.HasExtension(unittest::default_int32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_int64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_uint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_uint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_sint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_sint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_fixed32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_fixed64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_sfixed32_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_sfixed64_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_float_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_double_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_bool_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_string_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_bytes_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::default_nested_enum_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_foreign_enum_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_import_enum_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::default_string_piece_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_cord_extension)); + + // Fields with defaults have their default values (duh). + EXPECT_EQ( 41 , message.GetExtension(unittest::default_int32_extension )); + EXPECT_EQ( 42 , message.GetExtension(unittest::default_int64_extension )); + EXPECT_EQ( 43 , message.GetExtension(unittest::default_uint32_extension )); + EXPECT_EQ( 44 , message.GetExtension(unittest::default_uint64_extension )); + EXPECT_EQ(-45 , message.GetExtension(unittest::default_sint32_extension )); + EXPECT_EQ( 46 , message.GetExtension(unittest::default_sint64_extension )); + EXPECT_EQ( 47 , message.GetExtension(unittest::default_fixed32_extension )); + EXPECT_EQ( 48 , message.GetExtension(unittest::default_fixed64_extension )); + EXPECT_EQ( 49 , message.GetExtension(unittest::default_sfixed32_extension)); + EXPECT_EQ(-50 , message.GetExtension(unittest::default_sfixed64_extension)); + EXPECT_EQ( 51.5 , message.GetExtension(unittest::default_float_extension )); + EXPECT_EQ( 52e3 , message.GetExtension(unittest::default_double_extension )); + EXPECT_EQ(true , message.GetExtension(unittest::default_bool_extension )); + EXPECT_EQ("hello", message.GetExtension(unittest::default_string_extension )); + EXPECT_EQ("world", message.GetExtension(unittest::default_bytes_extension )); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::default_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_BAR , message.GetExtension(unittest::default_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::default_import_enum_extension )); + + EXPECT_EQ("abc", message.GetExtension(unittest::default_string_piece_extension)); + EXPECT_EQ("123", message.GetExtension(unittest::default_cord_extension)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectRepeatedExtensionsModified( + const unittest::TestAllExtensions& message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed32_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed64_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_float_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_double_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bool_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bytes_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeatedgroup_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_message_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_enum_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_piece_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_cord_extension)); + + EXPECT_EQ(201 , message.GetExtension(unittest::repeated_int32_extension , 0)); + EXPECT_EQ(202 , message.GetExtension(unittest::repeated_int64_extension , 0)); + EXPECT_EQ(203 , message.GetExtension(unittest::repeated_uint32_extension , 0)); + EXPECT_EQ(204 , message.GetExtension(unittest::repeated_uint64_extension , 0)); + EXPECT_EQ(205 , message.GetExtension(unittest::repeated_sint32_extension , 0)); + EXPECT_EQ(206 , message.GetExtension(unittest::repeated_sint64_extension , 0)); + EXPECT_EQ(207 , message.GetExtension(unittest::repeated_fixed32_extension , 0)); + EXPECT_EQ(208 , message.GetExtension(unittest::repeated_fixed64_extension , 0)); + EXPECT_EQ(209 , message.GetExtension(unittest::repeated_sfixed32_extension, 0)); + EXPECT_EQ(210 , message.GetExtension(unittest::repeated_sfixed64_extension, 0)); + EXPECT_EQ(211 , message.GetExtension(unittest::repeated_float_extension , 0)); + EXPECT_EQ(212 , message.GetExtension(unittest::repeated_double_extension , 0)); + EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension , 0)); + EXPECT_EQ("215", message.GetExtension(unittest::repeated_string_extension , 0)); + EXPECT_EQ("216", message.GetExtension(unittest::repeated_bytes_extension , 0)); + + EXPECT_EQ(217, message.GetExtension(unittest::repeatedgroup_extension , 0).a()); + EXPECT_EQ(218, message.GetExtension(unittest::repeated_nested_message_extension , 0).bb()); + EXPECT_EQ(219, message.GetExtension(unittest::repeated_foreign_message_extension, 0).c()); + EXPECT_EQ(220, message.GetExtension(unittest::repeated_import_message_extension , 0).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::repeated_nested_enum_extension , 0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.GetExtension(unittest::repeated_foreign_enum_extension, 0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::repeated_import_enum_extension , 0)); + + EXPECT_EQ("224", message.GetExtension(unittest::repeated_string_piece_extension, 0)); + EXPECT_EQ("225", message.GetExtension(unittest::repeated_cord_extension, 0)); + + // Actually verify the second (modified) elements now. + EXPECT_EQ(501 , message.GetExtension(unittest::repeated_int32_extension , 1)); + EXPECT_EQ(502 , message.GetExtension(unittest::repeated_int64_extension , 1)); + EXPECT_EQ(503 , message.GetExtension(unittest::repeated_uint32_extension , 1)); + EXPECT_EQ(504 , message.GetExtension(unittest::repeated_uint64_extension , 1)); + EXPECT_EQ(505 , message.GetExtension(unittest::repeated_sint32_extension , 1)); + EXPECT_EQ(506 , message.GetExtension(unittest::repeated_sint64_extension , 1)); + EXPECT_EQ(507 , message.GetExtension(unittest::repeated_fixed32_extension , 1)); + EXPECT_EQ(508 , message.GetExtension(unittest::repeated_fixed64_extension , 1)); + EXPECT_EQ(509 , message.GetExtension(unittest::repeated_sfixed32_extension, 1)); + EXPECT_EQ(510 , message.GetExtension(unittest::repeated_sfixed64_extension, 1)); + EXPECT_EQ(511 , message.GetExtension(unittest::repeated_float_extension , 1)); + EXPECT_EQ(512 , message.GetExtension(unittest::repeated_double_extension , 1)); + EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension , 1)); + EXPECT_EQ("515", message.GetExtension(unittest::repeated_string_extension , 1)); + EXPECT_EQ("516", message.GetExtension(unittest::repeated_bytes_extension , 1)); + + EXPECT_EQ(517, message.GetExtension(unittest::repeatedgroup_extension , 1).a()); + EXPECT_EQ(518, message.GetExtension(unittest::repeated_nested_message_extension , 1).bb()); + EXPECT_EQ(519, message.GetExtension(unittest::repeated_foreign_message_extension, 1).c()); + EXPECT_EQ(520, message.GetExtension(unittest::repeated_import_message_extension , 1).d()); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.GetExtension(unittest::repeated_nested_enum_extension , 1)); + EXPECT_EQ(unittest::FOREIGN_FOO , message.GetExtension(unittest::repeated_foreign_enum_extension, 1)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.GetExtension(unittest::repeated_import_enum_extension , 1)); + + EXPECT_EQ("524", message.GetExtension(unittest::repeated_string_piece_extension, 1)); + EXPECT_EQ("525", message.GetExtension(unittest::repeated_cord_extension, 1)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectAllFieldsAndExtensionsInOrder(const string& serialized) { + // We set each field individually, serialize separately, and concatenate all + // the strings in canonical order to determine the expected serialization. + string expected; + unittest::TestFieldOrderings message; + message.set_my_int(1); // Field 1. + message.AppendToString(&expected); + message.Clear(); + message.SetExtension(unittest::my_extension_int, 23); // Field 5. + message.AppendToString(&expected); + message.Clear(); + message.set_my_string("foo"); // Field 11. + message.AppendToString(&expected); + message.Clear(); + message.SetExtension(unittest::my_extension_string, "bar"); // Field 50. + message.AppendToString(&expected); + message.Clear(); + message.set_my_float(1.0); // Field 101. + message.AppendToString(&expected); + message.Clear(); + + // We don't EXPECT_EQ() since we don't want to print raw bytes to stdout. + EXPECT_TRUE(serialized == expected); +} + +// =================================================================== + +TestUtil::ReflectionTester::ReflectionTester( + const Descriptor* base_descriptor) + : base_descriptor_(base_descriptor) { + + const DescriptorPool* pool = base_descriptor->file()->pool(); + + nested_b_ = + pool->FindFieldByName("protobuf_unittest.TestAllTypes.NestedMessage.bb"); + foreign_c_ = + pool->FindFieldByName("protobuf_unittest.ForeignMessage.c"); + import_d_ = + pool->FindFieldByName("protobuf_unittest_import.ImportMessage.d"); + nested_foo_ = + pool->FindEnumValueByName("protobuf_unittest.TestAllTypes.FOO"); + nested_bar_ = + pool->FindEnumValueByName("protobuf_unittest.TestAllTypes.BAR"); + nested_baz_ = + pool->FindEnumValueByName("protobuf_unittest.TestAllTypes.BAZ"); + foreign_foo_ = + pool->FindEnumValueByName("protobuf_unittest.FOREIGN_FOO"); + foreign_bar_ = + pool->FindEnumValueByName("protobuf_unittest.FOREIGN_BAR"); + foreign_baz_ = + pool->FindEnumValueByName("protobuf_unittest.FOREIGN_BAZ"); + import_foo_ = + pool->FindEnumValueByName("protobuf_unittest_import.IMPORT_FOO"); + import_bar_ = + pool->FindEnumValueByName("protobuf_unittest_import.IMPORT_BAR"); + import_baz_ = + pool->FindEnumValueByName("protobuf_unittest_import.IMPORT_BAZ"); + + if (base_descriptor_->name() == "TestAllExtensions") { + group_a_ = + pool->FindFieldByName("protobuf_unittest.OptionalGroup_extension.a"); + repeated_group_a_ = + pool->FindFieldByName("protobuf_unittest.RepeatedGroup_extension.a"); + } else { + group_a_ = + pool->FindFieldByName("protobuf_unittest.TestAllTypes.OptionalGroup.a"); + repeated_group_a_ = + pool->FindFieldByName("protobuf_unittest.TestAllTypes.RepeatedGroup.a"); + } + + EXPECT_TRUE(group_a_ != NULL); + EXPECT_TRUE(repeated_group_a_ != NULL); + EXPECT_TRUE(nested_b_ != NULL); + EXPECT_TRUE(foreign_c_ != NULL); + EXPECT_TRUE(import_d_ != NULL); + EXPECT_TRUE(nested_foo_ != NULL); + EXPECT_TRUE(nested_bar_ != NULL); + EXPECT_TRUE(nested_baz_ != NULL); + EXPECT_TRUE(foreign_foo_ != NULL); + EXPECT_TRUE(foreign_bar_ != NULL); + EXPECT_TRUE(foreign_baz_ != NULL); + EXPECT_TRUE(import_foo_ != NULL); + EXPECT_TRUE(import_bar_ != NULL); + EXPECT_TRUE(import_baz_ != NULL); +} + +// Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes. +const FieldDescriptor* TestUtil::ReflectionTester::F(const string& name) { + const FieldDescriptor* result = NULL; + if (base_descriptor_->name() == "TestAllExtensions") { + result = base_descriptor_->file()->FindExtensionByName(name + "_extension"); + } else { + result = base_descriptor_->FindFieldByName(name); + } + GOOGLE_CHECK(result != NULL); + return result; +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::SetAllFieldsViaReflection( + Message::Reflection* message) { + message->SetInt32 (F("optional_int32" ), 101); + message->SetInt64 (F("optional_int64" ), 102); + message->SetUInt32(F("optional_uint32" ), 103); + message->SetUInt64(F("optional_uint64" ), 104); + message->SetInt32 (F("optional_sint32" ), 105); + message->SetInt64 (F("optional_sint64" ), 106); + message->SetUInt32(F("optional_fixed32" ), 107); + message->SetUInt64(F("optional_fixed64" ), 108); + message->SetInt32 (F("optional_sfixed32"), 109); + message->SetInt64 (F("optional_sfixed64"), 110); + message->SetFloat (F("optional_float" ), 111); + message->SetDouble(F("optional_double" ), 112); + message->SetBool (F("optional_bool" ), true); + message->SetString(F("optional_string" ), "115"); + message->SetString(F("optional_bytes" ), "116"); + + message->MutableMessage(F("optionalgroup")) + ->GetReflection()->SetInt32(group_a_, 117); + message->MutableMessage(F("optional_nested_message")) + ->GetReflection()->SetInt32(nested_b_, 118); + message->MutableMessage(F("optional_foreign_message")) + ->GetReflection()->SetInt32(foreign_c_, 119); + message->MutableMessage(F("optional_import_message")) + ->GetReflection()->SetInt32(import_d_, 120); + + message->SetEnum(F("optional_nested_enum" ), nested_baz_); + message->SetEnum(F("optional_foreign_enum"), foreign_baz_); + message->SetEnum(F("optional_import_enum" ), import_baz_); + + message->SetString(F("optional_string_piece"), "124"); + message->SetString(F("optional_cord"), "125"); + + // ----------------------------------------------------------------- + + message->AddInt32 (F("repeated_int32" ), 201); + message->AddInt64 (F("repeated_int64" ), 202); + message->AddUInt32(F("repeated_uint32" ), 203); + message->AddUInt64(F("repeated_uint64" ), 204); + message->AddInt32 (F("repeated_sint32" ), 205); + message->AddInt64 (F("repeated_sint64" ), 206); + message->AddUInt32(F("repeated_fixed32" ), 207); + message->AddUInt64(F("repeated_fixed64" ), 208); + message->AddInt32 (F("repeated_sfixed32"), 209); + message->AddInt64 (F("repeated_sfixed64"), 210); + message->AddFloat (F("repeated_float" ), 211); + message->AddDouble(F("repeated_double" ), 212); + message->AddBool (F("repeated_bool" ), true); + message->AddString(F("repeated_string" ), "215"); + message->AddString(F("repeated_bytes" ), "216"); + + message->AddMessage(F("repeatedgroup")) + ->GetReflection()->SetInt32(repeated_group_a_, 217); + message->AddMessage(F("repeated_nested_message")) + ->GetReflection()->SetInt32(nested_b_, 218); + message->AddMessage(F("repeated_foreign_message")) + ->GetReflection()->SetInt32(foreign_c_, 219); + message->AddMessage(F("repeated_import_message")) + ->GetReflection()->SetInt32(import_d_, 220); + + message->AddEnum(F("repeated_nested_enum" ), nested_bar_); + message->AddEnum(F("repeated_foreign_enum"), foreign_bar_); + message->AddEnum(F("repeated_import_enum" ), import_bar_); + + message->AddString(F("repeated_string_piece"), "224"); + message->AddString(F("repeated_cord"), "225"); + + // Add a second one of each field. + message->AddInt32 (F("repeated_int32" ), 301); + message->AddInt64 (F("repeated_int64" ), 302); + message->AddUInt32(F("repeated_uint32" ), 303); + message->AddUInt64(F("repeated_uint64" ), 304); + message->AddInt32 (F("repeated_sint32" ), 305); + message->AddInt64 (F("repeated_sint64" ), 306); + message->AddUInt32(F("repeated_fixed32" ), 307); + message->AddUInt64(F("repeated_fixed64" ), 308); + message->AddInt32 (F("repeated_sfixed32"), 309); + message->AddInt64 (F("repeated_sfixed64"), 310); + message->AddFloat (F("repeated_float" ), 311); + message->AddDouble(F("repeated_double" ), 312); + message->AddBool (F("repeated_bool" ), false); + message->AddString(F("repeated_string" ), "315"); + message->AddString(F("repeated_bytes" ), "316"); + + message->AddMessage(F("repeatedgroup")) + ->GetReflection()->SetInt32(repeated_group_a_, 317); + message->AddMessage(F("repeated_nested_message")) + ->GetReflection()->SetInt32(nested_b_, 318); + message->AddMessage(F("repeated_foreign_message")) + ->GetReflection()->SetInt32(foreign_c_, 319); + message->AddMessage(F("repeated_import_message")) + ->GetReflection()->SetInt32(import_d_, 320); + + message->AddEnum(F("repeated_nested_enum" ), nested_baz_); + message->AddEnum(F("repeated_foreign_enum"), foreign_baz_); + message->AddEnum(F("repeated_import_enum" ), import_baz_); + + message->AddString(F("repeated_string_piece"), "324"); + message->AddString(F("repeated_cord"), "325"); + + // ----------------------------------------------------------------- + + message->SetInt32 (F("default_int32" ), 401); + message->SetInt64 (F("default_int64" ), 402); + message->SetUInt32(F("default_uint32" ), 403); + message->SetUInt64(F("default_uint64" ), 404); + message->SetInt32 (F("default_sint32" ), 405); + message->SetInt64 (F("default_sint64" ), 406); + message->SetUInt32(F("default_fixed32" ), 407); + message->SetUInt64(F("default_fixed64" ), 408); + message->SetInt32 (F("default_sfixed32"), 409); + message->SetInt64 (F("default_sfixed64"), 410); + message->SetFloat (F("default_float" ), 411); + message->SetDouble(F("default_double" ), 412); + message->SetBool (F("default_bool" ), false); + message->SetString(F("default_string" ), "415"); + message->SetString(F("default_bytes" ), "416"); + + message->SetEnum(F("default_nested_enum" ), nested_foo_); + message->SetEnum(F("default_foreign_enum"), foreign_foo_); + message->SetEnum(F("default_import_enum" ), import_foo_); + + message->SetString(F("default_string_piece"), "424"); + message->SetString(F("default_cord"), "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::ExpectAllFieldsSetViaReflection( + const Message::Reflection& message) { + string scratch; + + EXPECT_TRUE(message.HasField(F("optional_int32" ))); + EXPECT_TRUE(message.HasField(F("optional_int64" ))); + EXPECT_TRUE(message.HasField(F("optional_uint32" ))); + EXPECT_TRUE(message.HasField(F("optional_uint64" ))); + EXPECT_TRUE(message.HasField(F("optional_sint32" ))); + EXPECT_TRUE(message.HasField(F("optional_sint64" ))); + EXPECT_TRUE(message.HasField(F("optional_fixed32" ))); + EXPECT_TRUE(message.HasField(F("optional_fixed64" ))); + EXPECT_TRUE(message.HasField(F("optional_sfixed32"))); + EXPECT_TRUE(message.HasField(F("optional_sfixed64"))); + EXPECT_TRUE(message.HasField(F("optional_float" ))); + EXPECT_TRUE(message.HasField(F("optional_double" ))); + EXPECT_TRUE(message.HasField(F("optional_bool" ))); + EXPECT_TRUE(message.HasField(F("optional_string" ))); + EXPECT_TRUE(message.HasField(F("optional_bytes" ))); + + EXPECT_TRUE(message.HasField(F("optionalgroup" ))); + EXPECT_TRUE(message.HasField(F("optional_nested_message" ))); + EXPECT_TRUE(message.HasField(F("optional_foreign_message"))); + EXPECT_TRUE(message.HasField(F("optional_import_message" ))); + + EXPECT_TRUE(message.GetMessage(F("optionalgroup")) + .GetReflection()->HasField(group_a_)); + EXPECT_TRUE(message.GetMessage(F("optional_nested_message")) + .GetReflection()->HasField(nested_b_)); + EXPECT_TRUE(message.GetMessage(F("optional_foreign_message")) + .GetReflection()->HasField(foreign_c_)); + EXPECT_TRUE(message.GetMessage(F("optional_import_message")) + .GetReflection()->HasField(import_d_)); + + EXPECT_TRUE(message.HasField(F("optional_nested_enum" ))); + EXPECT_TRUE(message.HasField(F("optional_foreign_enum"))); + EXPECT_TRUE(message.HasField(F("optional_import_enum" ))); + + EXPECT_TRUE(message.HasField(F("optional_string_piece"))); + EXPECT_TRUE(message.HasField(F("optional_cord"))); + + EXPECT_EQ(101 , message.GetInt32 (F("optional_int32" ))); + EXPECT_EQ(102 , message.GetInt64 (F("optional_int64" ))); + EXPECT_EQ(103 , message.GetUInt32(F("optional_uint32" ))); + EXPECT_EQ(104 , message.GetUInt64(F("optional_uint64" ))); + EXPECT_EQ(105 , message.GetInt32 (F("optional_sint32" ))); + EXPECT_EQ(106 , message.GetInt64 (F("optional_sint64" ))); + EXPECT_EQ(107 , message.GetUInt32(F("optional_fixed32" ))); + EXPECT_EQ(108 , message.GetUInt64(F("optional_fixed64" ))); + EXPECT_EQ(109 , message.GetInt32 (F("optional_sfixed32"))); + EXPECT_EQ(110 , message.GetInt64 (F("optional_sfixed64"))); + EXPECT_EQ(111 , message.GetFloat (F("optional_float" ))); + EXPECT_EQ(112 , message.GetDouble(F("optional_double" ))); + EXPECT_EQ(true , message.GetBool (F("optional_bool" ))); + EXPECT_EQ("115", message.GetString(F("optional_string" ))); + EXPECT_EQ("116", message.GetString(F("optional_bytes" ))); + + EXPECT_EQ("115", message.GetStringReference(F("optional_string"), &scratch)); + EXPECT_EQ("116", message.GetStringReference(F("optional_bytes" ), &scratch)); + + EXPECT_EQ(117, message.GetMessage(F("optionalgroup")) + .GetReflection()->GetInt32(group_a_)); + EXPECT_EQ(118, message.GetMessage(F("optional_nested_message")) + .GetReflection()->GetInt32(nested_b_)); + EXPECT_EQ(119, message.GetMessage(F("optional_foreign_message")) + .GetReflection()->GetInt32(foreign_c_)); + EXPECT_EQ(120, message.GetMessage(F("optional_import_message")) + .GetReflection()->GetInt32(import_d_)); + + EXPECT_EQ( nested_baz_, message.GetEnum(F("optional_nested_enum" ))); + EXPECT_EQ(foreign_baz_, message.GetEnum(F("optional_foreign_enum"))); + EXPECT_EQ( import_baz_, message.GetEnum(F("optional_import_enum" ))); + + EXPECT_EQ("124", message.GetString(F("optional_string_piece"))); + EXPECT_EQ("124", message.GetStringReference(F("optional_string_piece"), + &scratch)); + + EXPECT_EQ("125", message.GetString(F("optional_cord"))); + EXPECT_EQ("125", message.GetStringReference(F("optional_cord"), + &scratch)); + + // ----------------------------------------------------------------- + + ASSERT_EQ(2, message.FieldSize(F("repeated_int32" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_int64" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_uint32" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_uint64" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_sint32" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_sint64" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_fixed32" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_fixed64" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_sfixed32"))); + ASSERT_EQ(2, message.FieldSize(F("repeated_sfixed64"))); + ASSERT_EQ(2, message.FieldSize(F("repeated_float" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_double" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_bool" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_string" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_bytes" ))); + + ASSERT_EQ(2, message.FieldSize(F("repeatedgroup" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_nested_message" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_foreign_message"))); + ASSERT_EQ(2, message.FieldSize(F("repeated_import_message" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_nested_enum" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_foreign_enum" ))); + ASSERT_EQ(2, message.FieldSize(F("repeated_import_enum" ))); + + ASSERT_EQ(2, message.FieldSize(F("repeated_string_piece"))); + ASSERT_EQ(2, message.FieldSize(F("repeated_cord"))); + + EXPECT_EQ(201 , message.GetRepeatedInt32 (F("repeated_int32" ), 0)); + EXPECT_EQ(202 , message.GetRepeatedInt64 (F("repeated_int64" ), 0)); + EXPECT_EQ(203 , message.GetRepeatedUInt32(F("repeated_uint32" ), 0)); + EXPECT_EQ(204 , message.GetRepeatedUInt64(F("repeated_uint64" ), 0)); + EXPECT_EQ(205 , message.GetRepeatedInt32 (F("repeated_sint32" ), 0)); + EXPECT_EQ(206 , message.GetRepeatedInt64 (F("repeated_sint64" ), 0)); + EXPECT_EQ(207 , message.GetRepeatedUInt32(F("repeated_fixed32" ), 0)); + EXPECT_EQ(208 , message.GetRepeatedUInt64(F("repeated_fixed64" ), 0)); + EXPECT_EQ(209 , message.GetRepeatedInt32 (F("repeated_sfixed32"), 0)); + EXPECT_EQ(210 , message.GetRepeatedInt64 (F("repeated_sfixed64"), 0)); + EXPECT_EQ(211 , message.GetRepeatedFloat (F("repeated_float" ), 0)); + EXPECT_EQ(212 , message.GetRepeatedDouble(F("repeated_double" ), 0)); + EXPECT_EQ(true , message.GetRepeatedBool (F("repeated_bool" ), 0)); + EXPECT_EQ("215", message.GetRepeatedString(F("repeated_string" ), 0)); + EXPECT_EQ("216", message.GetRepeatedString(F("repeated_bytes" ), 0)); + + EXPECT_EQ("215", message.GetRepeatedStringReference(F("repeated_string"), + 0, &scratch)); + EXPECT_EQ("216", message.GetRepeatedStringReference(F("repeated_bytes"), + 0, &scratch)); + + EXPECT_EQ(217, message.GetRepeatedMessage(F("repeatedgroup"), 0) + .GetReflection()->GetInt32(repeated_group_a_)); + EXPECT_EQ(218, message.GetRepeatedMessage(F("repeated_nested_message"), 0) + .GetReflection()->GetInt32(nested_b_)); + EXPECT_EQ(219, message.GetRepeatedMessage(F("repeated_foreign_message"), 0) + .GetReflection()->GetInt32(foreign_c_)); + EXPECT_EQ(220, message.GetRepeatedMessage(F("repeated_import_message"), 0) + .GetReflection()->GetInt32(import_d_)); + + EXPECT_EQ( nested_bar_, message.GetRepeatedEnum(F("repeated_nested_enum" ),0)); + EXPECT_EQ(foreign_bar_, message.GetRepeatedEnum(F("repeated_foreign_enum"),0)); + EXPECT_EQ( import_bar_, message.GetRepeatedEnum(F("repeated_import_enum" ),0)); + + EXPECT_EQ("224", message.GetRepeatedString(F("repeated_string_piece"), 0)); + EXPECT_EQ("224", message.GetRepeatedStringReference( + F("repeated_string_piece"), 0, &scratch)); + + EXPECT_EQ("225", message.GetRepeatedString(F("repeated_cord"), 0)); + EXPECT_EQ("225", message.GetRepeatedStringReference( + F("repeated_cord"), 0, &scratch)); + + EXPECT_EQ(301 , message.GetRepeatedInt32 (F("repeated_int32" ), 1)); + EXPECT_EQ(302 , message.GetRepeatedInt64 (F("repeated_int64" ), 1)); + EXPECT_EQ(303 , message.GetRepeatedUInt32(F("repeated_uint32" ), 1)); + EXPECT_EQ(304 , message.GetRepeatedUInt64(F("repeated_uint64" ), 1)); + EXPECT_EQ(305 , message.GetRepeatedInt32 (F("repeated_sint32" ), 1)); + EXPECT_EQ(306 , message.GetRepeatedInt64 (F("repeated_sint64" ), 1)); + EXPECT_EQ(307 , message.GetRepeatedUInt32(F("repeated_fixed32" ), 1)); + EXPECT_EQ(308 , message.GetRepeatedUInt64(F("repeated_fixed64" ), 1)); + EXPECT_EQ(309 , message.GetRepeatedInt32 (F("repeated_sfixed32"), 1)); + EXPECT_EQ(310 , message.GetRepeatedInt64 (F("repeated_sfixed64"), 1)); + EXPECT_EQ(311 , message.GetRepeatedFloat (F("repeated_float" ), 1)); + EXPECT_EQ(312 , message.GetRepeatedDouble(F("repeated_double" ), 1)); + EXPECT_EQ(false, message.GetRepeatedBool (F("repeated_bool" ), 1)); + EXPECT_EQ("315", message.GetRepeatedString(F("repeated_string" ), 1)); + EXPECT_EQ("316", message.GetRepeatedString(F("repeated_bytes" ), 1)); + + EXPECT_EQ("315", message.GetRepeatedStringReference(F("repeated_string"), + 1, &scratch)); + EXPECT_EQ("316", message.GetRepeatedStringReference(F("repeated_bytes"), + 1, &scratch)); + + EXPECT_EQ(317, message.GetRepeatedMessage(F("repeatedgroup"), 1) + .GetReflection()->GetInt32(repeated_group_a_)); + EXPECT_EQ(318, message.GetRepeatedMessage(F("repeated_nested_message"), 1) + .GetReflection()->GetInt32(nested_b_)); + EXPECT_EQ(319, message.GetRepeatedMessage(F("repeated_foreign_message"), 1) + .GetReflection()->GetInt32(foreign_c_)); + EXPECT_EQ(320, message.GetRepeatedMessage(F("repeated_import_message"), 1) + .GetReflection()->GetInt32(import_d_)); + + EXPECT_EQ( nested_baz_, message.GetRepeatedEnum(F("repeated_nested_enum" ),1)); + EXPECT_EQ(foreign_baz_, message.GetRepeatedEnum(F("repeated_foreign_enum"),1)); + EXPECT_EQ( import_baz_, message.GetRepeatedEnum(F("repeated_import_enum" ),1)); + + EXPECT_EQ("324", message.GetRepeatedString(F("repeated_string_piece"), 1)); + EXPECT_EQ("324", message.GetRepeatedStringReference( + F("repeated_string_piece"), 1, &scratch)); + + EXPECT_EQ("325", message.GetRepeatedString(F("repeated_cord"), 1)); + EXPECT_EQ("325", message.GetRepeatedStringReference( + F("repeated_cord"), 1, &scratch)); + + // ----------------------------------------------------------------- + + EXPECT_TRUE(message.HasField(F("default_int32" ))); + EXPECT_TRUE(message.HasField(F("default_int64" ))); + EXPECT_TRUE(message.HasField(F("default_uint32" ))); + EXPECT_TRUE(message.HasField(F("default_uint64" ))); + EXPECT_TRUE(message.HasField(F("default_sint32" ))); + EXPECT_TRUE(message.HasField(F("default_sint64" ))); + EXPECT_TRUE(message.HasField(F("default_fixed32" ))); + EXPECT_TRUE(message.HasField(F("default_fixed64" ))); + EXPECT_TRUE(message.HasField(F("default_sfixed32"))); + EXPECT_TRUE(message.HasField(F("default_sfixed64"))); + EXPECT_TRUE(message.HasField(F("default_float" ))); + EXPECT_TRUE(message.HasField(F("default_double" ))); + EXPECT_TRUE(message.HasField(F("default_bool" ))); + EXPECT_TRUE(message.HasField(F("default_string" ))); + EXPECT_TRUE(message.HasField(F("default_bytes" ))); + + EXPECT_TRUE(message.HasField(F("default_nested_enum" ))); + EXPECT_TRUE(message.HasField(F("default_foreign_enum"))); + EXPECT_TRUE(message.HasField(F("default_import_enum" ))); + + EXPECT_TRUE(message.HasField(F("default_string_piece"))); + EXPECT_TRUE(message.HasField(F("default_cord"))); + + EXPECT_EQ(401 , message.GetInt32 (F("default_int32" ))); + EXPECT_EQ(402 , message.GetInt64 (F("default_int64" ))); + EXPECT_EQ(403 , message.GetUInt32(F("default_uint32" ))); + EXPECT_EQ(404 , message.GetUInt64(F("default_uint64" ))); + EXPECT_EQ(405 , message.GetInt32 (F("default_sint32" ))); + EXPECT_EQ(406 , message.GetInt64 (F("default_sint64" ))); + EXPECT_EQ(407 , message.GetUInt32(F("default_fixed32" ))); + EXPECT_EQ(408 , message.GetUInt64(F("default_fixed64" ))); + EXPECT_EQ(409 , message.GetInt32 (F("default_sfixed32"))); + EXPECT_EQ(410 , message.GetInt64 (F("default_sfixed64"))); + EXPECT_EQ(411 , message.GetFloat (F("default_float" ))); + EXPECT_EQ(412 , message.GetDouble(F("default_double" ))); + EXPECT_EQ(false, message.GetBool (F("default_bool" ))); + EXPECT_EQ("415", message.GetString(F("default_string" ))); + EXPECT_EQ("416", message.GetString(F("default_bytes" ))); + + EXPECT_EQ("415", message.GetStringReference(F("default_string"), &scratch)); + EXPECT_EQ("416", message.GetStringReference(F("default_bytes" ), &scratch)); + + EXPECT_EQ( nested_foo_, message.GetEnum(F("default_nested_enum" ))); + EXPECT_EQ(foreign_foo_, message.GetEnum(F("default_foreign_enum"))); + EXPECT_EQ( import_foo_, message.GetEnum(F("default_import_enum" ))); + + EXPECT_EQ("424", message.GetString(F("default_string_piece"))); + EXPECT_EQ("424", message.GetStringReference(F("default_string_piece"), + &scratch)); + + EXPECT_EQ("425", message.GetString(F("default_cord"))); + EXPECT_EQ("425", message.GetStringReference(F("default_cord"), &scratch)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::ExpectClearViaReflection( + const Message::Reflection& message) { + string scratch; + + // has_blah() should initially be false for all optional fields. + EXPECT_FALSE(message.HasField(F("optional_int32" ))); + EXPECT_FALSE(message.HasField(F("optional_int64" ))); + EXPECT_FALSE(message.HasField(F("optional_uint32" ))); + EXPECT_FALSE(message.HasField(F("optional_uint64" ))); + EXPECT_FALSE(message.HasField(F("optional_sint32" ))); + EXPECT_FALSE(message.HasField(F("optional_sint64" ))); + EXPECT_FALSE(message.HasField(F("optional_fixed32" ))); + EXPECT_FALSE(message.HasField(F("optional_fixed64" ))); + EXPECT_FALSE(message.HasField(F("optional_sfixed32"))); + EXPECT_FALSE(message.HasField(F("optional_sfixed64"))); + EXPECT_FALSE(message.HasField(F("optional_float" ))); + EXPECT_FALSE(message.HasField(F("optional_double" ))); + EXPECT_FALSE(message.HasField(F("optional_bool" ))); + EXPECT_FALSE(message.HasField(F("optional_string" ))); + EXPECT_FALSE(message.HasField(F("optional_bytes" ))); + + EXPECT_FALSE(message.HasField(F("optionalgroup" ))); + EXPECT_FALSE(message.HasField(F("optional_nested_message" ))); + EXPECT_FALSE(message.HasField(F("optional_foreign_message"))); + EXPECT_FALSE(message.HasField(F("optional_import_message" ))); + + EXPECT_FALSE(message.HasField(F("optional_nested_enum" ))); + EXPECT_FALSE(message.HasField(F("optional_foreign_enum"))); + EXPECT_FALSE(message.HasField(F("optional_import_enum" ))); + + EXPECT_FALSE(message.HasField(F("optional_string_piece"))); + EXPECT_FALSE(message.HasField(F("optional_cord"))); + + // Optional fields without defaults are set to zero or something like it. + EXPECT_EQ(0 , message.GetInt32 (F("optional_int32" ))); + EXPECT_EQ(0 , message.GetInt64 (F("optional_int64" ))); + EXPECT_EQ(0 , message.GetUInt32(F("optional_uint32" ))); + EXPECT_EQ(0 , message.GetUInt64(F("optional_uint64" ))); + EXPECT_EQ(0 , message.GetInt32 (F("optional_sint32" ))); + EXPECT_EQ(0 , message.GetInt64 (F("optional_sint64" ))); + EXPECT_EQ(0 , message.GetUInt32(F("optional_fixed32" ))); + EXPECT_EQ(0 , message.GetUInt64(F("optional_fixed64" ))); + EXPECT_EQ(0 , message.GetInt32 (F("optional_sfixed32"))); + EXPECT_EQ(0 , message.GetInt64 (F("optional_sfixed64"))); + EXPECT_EQ(0 , message.GetFloat (F("optional_float" ))); + EXPECT_EQ(0 , message.GetDouble(F("optional_double" ))); + EXPECT_EQ(false, message.GetBool (F("optional_bool" ))); + EXPECT_EQ("" , message.GetString(F("optional_string" ))); + EXPECT_EQ("" , message.GetString(F("optional_bytes" ))); + + EXPECT_EQ("", message.GetStringReference(F("optional_string"), &scratch)); + EXPECT_EQ("", message.GetStringReference(F("optional_bytes" ), &scratch)); + + // Embedded messages should also be clear. + EXPECT_FALSE(message.GetMessage(F("optionalgroup")) + .GetReflection()->HasField(group_a_)); + EXPECT_FALSE(message.GetMessage(F("optional_nested_message")) + .GetReflection()->HasField(nested_b_)); + EXPECT_FALSE(message.GetMessage(F("optional_foreign_message")) + .GetReflection()->HasField(foreign_c_)); + EXPECT_FALSE(message.GetMessage(F("optional_import_message")) + .GetReflection()->HasField(import_d_)); + + EXPECT_EQ(0, message.GetMessage(F("optionalgroup")) + .GetReflection()->GetInt32(group_a_)); + EXPECT_EQ(0, message.GetMessage(F("optional_nested_message")) + .GetReflection()->GetInt32(nested_b_)); + EXPECT_EQ(0, message.GetMessage(F("optional_foreign_message")) + .GetReflection()->GetInt32(foreign_c_)); + EXPECT_EQ(0, message.GetMessage(F("optional_import_message")) + .GetReflection()->GetInt32(import_d_)); + + // Enums without defaults are set to the first value in the enum. + EXPECT_EQ( nested_foo_, message.GetEnum(F("optional_nested_enum" ))); + EXPECT_EQ(foreign_foo_, message.GetEnum(F("optional_foreign_enum"))); + EXPECT_EQ( import_foo_, message.GetEnum(F("optional_import_enum" ))); + + EXPECT_EQ("", message.GetString(F("optional_string_piece"))); + EXPECT_EQ("", message.GetStringReference(F("optional_string_piece"), + &scratch)); + + EXPECT_EQ("", message.GetString(F("optional_cord"))); + EXPECT_EQ("", message.GetStringReference(F("optional_cord"), &scratch)); + + // Repeated fields are empty. + EXPECT_EQ(0, message.FieldSize(F("repeated_int32" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_int64" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_uint32" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_uint64" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_sint32" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_sint64" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_fixed32" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_fixed64" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_sfixed32"))); + EXPECT_EQ(0, message.FieldSize(F("repeated_sfixed64"))); + EXPECT_EQ(0, message.FieldSize(F("repeated_float" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_double" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_bool" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_string" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_bytes" ))); + + EXPECT_EQ(0, message.FieldSize(F("repeatedgroup" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_nested_message" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_foreign_message"))); + EXPECT_EQ(0, message.FieldSize(F("repeated_import_message" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_nested_enum" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_foreign_enum" ))); + EXPECT_EQ(0, message.FieldSize(F("repeated_import_enum" ))); + + EXPECT_EQ(0, message.FieldSize(F("repeated_string_piece"))); + EXPECT_EQ(0, message.FieldSize(F("repeated_cord"))); + + // has_blah() should also be false for all default fields. + EXPECT_FALSE(message.HasField(F("default_int32" ))); + EXPECT_FALSE(message.HasField(F("default_int64" ))); + EXPECT_FALSE(message.HasField(F("default_uint32" ))); + EXPECT_FALSE(message.HasField(F("default_uint64" ))); + EXPECT_FALSE(message.HasField(F("default_sint32" ))); + EXPECT_FALSE(message.HasField(F("default_sint64" ))); + EXPECT_FALSE(message.HasField(F("default_fixed32" ))); + EXPECT_FALSE(message.HasField(F("default_fixed64" ))); + EXPECT_FALSE(message.HasField(F("default_sfixed32"))); + EXPECT_FALSE(message.HasField(F("default_sfixed64"))); + EXPECT_FALSE(message.HasField(F("default_float" ))); + EXPECT_FALSE(message.HasField(F("default_double" ))); + EXPECT_FALSE(message.HasField(F("default_bool" ))); + EXPECT_FALSE(message.HasField(F("default_string" ))); + EXPECT_FALSE(message.HasField(F("default_bytes" ))); + + EXPECT_FALSE(message.HasField(F("default_nested_enum" ))); + EXPECT_FALSE(message.HasField(F("default_foreign_enum"))); + EXPECT_FALSE(message.HasField(F("default_import_enum" ))); + + EXPECT_FALSE(message.HasField(F("default_string_piece"))); + EXPECT_FALSE(message.HasField(F("default_cord"))); + + // Fields with defaults have their default values (duh). + EXPECT_EQ( 41 , message.GetInt32 (F("default_int32" ))); + EXPECT_EQ( 42 , message.GetInt64 (F("default_int64" ))); + EXPECT_EQ( 43 , message.GetUInt32(F("default_uint32" ))); + EXPECT_EQ( 44 , message.GetUInt64(F("default_uint64" ))); + EXPECT_EQ(-45 , message.GetInt32 (F("default_sint32" ))); + EXPECT_EQ( 46 , message.GetInt64 (F("default_sint64" ))); + EXPECT_EQ( 47 , message.GetUInt32(F("default_fixed32" ))); + EXPECT_EQ( 48 , message.GetUInt64(F("default_fixed64" ))); + EXPECT_EQ( 49 , message.GetInt32 (F("default_sfixed32"))); + EXPECT_EQ(-50 , message.GetInt64 (F("default_sfixed64"))); + EXPECT_EQ( 51.5 , message.GetFloat (F("default_float" ))); + EXPECT_EQ( 52e3 , message.GetDouble(F("default_double" ))); + EXPECT_EQ(true , message.GetBool (F("default_bool" ))); + EXPECT_EQ("hello", message.GetString(F("default_string" ))); + EXPECT_EQ("world", message.GetString(F("default_bytes" ))); + + EXPECT_EQ("hello", message.GetStringReference(F("default_string"), &scratch)); + EXPECT_EQ("world", message.GetStringReference(F("default_bytes" ), &scratch)); + + EXPECT_EQ( nested_bar_, message.GetEnum(F("default_nested_enum" ))); + EXPECT_EQ(foreign_bar_, message.GetEnum(F("default_foreign_enum"))); + EXPECT_EQ( import_bar_, message.GetEnum(F("default_import_enum" ))); + + EXPECT_EQ("abc", message.GetString(F("default_string_piece"))); + EXPECT_EQ("abc", message.GetStringReference(F("default_string_piece"), + &scratch)); + + EXPECT_EQ("123", message.GetString(F("default_cord"))); + EXPECT_EQ("123", message.GetStringReference(F("default_cord"), &scratch)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::ModifyRepeatedFieldsViaReflection( + Message::Reflection* message) { + message->SetRepeatedInt32 (F("repeated_int32" ), 1, 501); + message->SetRepeatedInt64 (F("repeated_int64" ), 1, 502); + message->SetRepeatedUInt32(F("repeated_uint32" ), 1, 503); + message->SetRepeatedUInt64(F("repeated_uint64" ), 1, 504); + message->SetRepeatedInt32 (F("repeated_sint32" ), 1, 505); + message->SetRepeatedInt64 (F("repeated_sint64" ), 1, 506); + message->SetRepeatedUInt32(F("repeated_fixed32" ), 1, 507); + message->SetRepeatedUInt64(F("repeated_fixed64" ), 1, 508); + message->SetRepeatedInt32 (F("repeated_sfixed32"), 1, 509); + message->SetRepeatedInt64 (F("repeated_sfixed64"), 1, 510); + message->SetRepeatedFloat (F("repeated_float" ), 1, 511); + message->SetRepeatedDouble(F("repeated_double" ), 1, 512); + message->SetRepeatedBool (F("repeated_bool" ), 1, true); + message->SetRepeatedString(F("repeated_string" ), 1, "515"); + message->SetRepeatedString(F("repeated_bytes" ), 1, "516"); + + message->MutableRepeatedMessage(F("repeatedgroup"), 1) + ->GetReflection()->SetInt32(repeated_group_a_, 517); + message->MutableRepeatedMessage(F("repeated_nested_message"), 1) + ->GetReflection()->SetInt32(nested_b_, 518); + message->MutableRepeatedMessage(F("repeated_foreign_message"), 1) + ->GetReflection()->SetInt32(foreign_c_, 519); + message->MutableRepeatedMessage(F("repeated_import_message"), 1) + ->GetReflection()->SetInt32(import_d_, 520); + + message->SetRepeatedEnum(F("repeated_nested_enum" ), 1, nested_foo_); + message->SetRepeatedEnum(F("repeated_foreign_enum"), 1, foreign_foo_); + message->SetRepeatedEnum(F("repeated_import_enum" ), 1, import_foo_); + + message->SetRepeatedString(F("repeated_string_piece"), 1, "524"); + message->SetRepeatedString(F("repeated_cord"), 1, "525"); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/test_util.h b/src/google/protobuf/test_util.h new file mode 100644 index 00000000..4157d095 --- /dev/null +++ b/src/google/protobuf/test_util.h @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_TEST_UTIL_H__ +#define GOOGLE_PROTOBUF_TEST_UTIL_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace unittest = protobuf_unittest; +namespace unittest_import = protobuf_unittest_import; + +class TestUtil { + public: + // Set every field in the message to a unique value. + static void SetAllFields(unittest::TestAllTypes* message); + static void SetAllExtensions(unittest::TestAllExtensions* message); + static void SetAllFieldsAndExtensions(unittest::TestFieldOrderings* message); + + // Use the repeated versions of the set_*() accessors to modify all the + // repeated fields of the messsage (which should already have been + // initialized with SetAllFields()). SetAllFields() itself only tests + // the add_*() accessors. + static void ModifyRepeatedFields(unittest::TestAllTypes* message); + static void ModifyRepeatedExtensions(unittest::TestAllExtensions* message); + + // Check that all fields have the values that they should have after + // SetAllFields() is called. + static void ExpectAllFieldsSet(const unittest::TestAllTypes& message); + static void ExpectAllExtensionsSet( + const unittest::TestAllExtensions& message); + + // Expect that the message is modified as would be expected from + // ModifyRepeatedFields(). + static void ExpectRepeatedFieldsModified( + const unittest::TestAllTypes& message); + static void ExpectRepeatedExtensionsModified( + const unittest::TestAllExtensions& message); + + // Check that all fields have their default values. + static void ExpectClear(const unittest::TestAllTypes& message); + static void ExpectExtensionsClear(const unittest::TestAllExtensions& message); + + // Check that the passed-in serialization is the canonical serialization we + // expect for a TestFieldOrderings message filled in by + // SetAllFieldsAndExtensions(). + static void ExpectAllFieldsAndExtensionsInOrder(const string& serialized); + + // Like above, but use the reflection interface. + class ReflectionTester { + public: + // base_descriptor must be a descriptor for TestAllTypes or + // TestAllExtensions. In the former case, ReflectionTester fetches from + // it the FieldDescriptors needed to use the reflection interface. In + // the latter case, ReflectionTester searches for extension fields in + // its file. + explicit ReflectionTester(const Descriptor* base_descriptor); + + void SetAllFieldsViaReflection(Message::Reflection* message); + void ModifyRepeatedFieldsViaReflection(Message::Reflection* message); + void ExpectAllFieldsSetViaReflection( + const Message::Reflection& message); + void ExpectClearViaReflection(const Message::Reflection& message); + + private: + const FieldDescriptor* F(const string& name); + + const Descriptor* base_descriptor_; + + const FieldDescriptor* group_a_; + const FieldDescriptor* repeated_group_a_; + const FieldDescriptor* nested_b_; + const FieldDescriptor* foreign_c_; + const FieldDescriptor* import_d_; + + const EnumValueDescriptor* nested_foo_; + const EnumValueDescriptor* nested_bar_; + const EnumValueDescriptor* nested_baz_; + const EnumValueDescriptor* foreign_foo_; + const EnumValueDescriptor* foreign_bar_; + const EnumValueDescriptor* foreign_baz_; + const EnumValueDescriptor* import_foo_; + const EnumValueDescriptor* import_bar_; + const EnumValueDescriptor* import_baz_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionTester); + }; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TestUtil); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_TEST_UTIL_H__ diff --git a/src/google/protobuf/testdata/golden_message b/src/google/protobuf/testdata/golden_message new file mode 100644 index 00000000..94898e49 Binary files /dev/null and b/src/google/protobuf/testdata/golden_message differ diff --git a/src/google/protobuf/testdata/text_format_unittest_data.txt b/src/google/protobuf/testdata/text_format_unittest_data.txt new file mode 100644 index 00000000..feea8f7b --- /dev/null +++ b/src/google/protobuf/testdata/text_format_unittest_data.txt @@ -0,0 +1,116 @@ +optional_int32: 101 +optional_int64: 102 +optional_uint32: 103 +optional_uint64: 104 +optional_sint32: 105 +optional_sint64: 106 +optional_fixed32: 107 +optional_fixed64: 108 +optional_sfixed32: 109 +optional_sfixed64: 110 +optional_float: 111 +optional_double: 112 +optional_bool: true +optional_string: "115" +optional_bytes: "116" +OptionalGroup { + a: 117 +} +optional_nested_message { + bb: 118 +} +optional_foreign_message { + c: 119 +} +optional_import_message { + d: 120 +} +optional_nested_enum: BAZ +optional_foreign_enum: FOREIGN_BAZ +optional_import_enum: IMPORT_BAZ +optional_string_piece: "124" +optional_cord: "125" +repeated_int32: 201 +repeated_int32: 301 +repeated_int64: 202 +repeated_int64: 302 +repeated_uint32: 203 +repeated_uint32: 303 +repeated_uint64: 204 +repeated_uint64: 304 +repeated_sint32: 205 +repeated_sint32: 305 +repeated_sint64: 206 +repeated_sint64: 306 +repeated_fixed32: 207 +repeated_fixed32: 307 +repeated_fixed64: 208 +repeated_fixed64: 308 +repeated_sfixed32: 209 +repeated_sfixed32: 309 +repeated_sfixed64: 210 +repeated_sfixed64: 310 +repeated_float: 211 +repeated_float: 311 +repeated_double: 212 +repeated_double: 312 +repeated_bool: true +repeated_bool: false +repeated_string: "215" +repeated_string: "315" +repeated_bytes: "216" +repeated_bytes: "316" +RepeatedGroup { + a: 217 +} +RepeatedGroup { + a: 317 +} +repeated_nested_message { + bb: 218 +} +repeated_nested_message { + bb: 318 +} +repeated_foreign_message { + c: 219 +} +repeated_foreign_message { + c: 319 +} +repeated_import_message { + d: 220 +} +repeated_import_message { + d: 320 +} +repeated_nested_enum: BAR +repeated_nested_enum: BAZ +repeated_foreign_enum: FOREIGN_BAR +repeated_foreign_enum: FOREIGN_BAZ +repeated_import_enum: IMPORT_BAR +repeated_import_enum: IMPORT_BAZ +repeated_string_piece: "224" +repeated_string_piece: "324" +repeated_cord: "225" +repeated_cord: "325" +default_int32: 401 +default_int64: 402 +default_uint32: 403 +default_uint64: 404 +default_sint32: 405 +default_sint64: 406 +default_fixed32: 407 +default_fixed64: 408 +default_sfixed32: 409 +default_sfixed64: 410 +default_float: 411 +default_double: 412 +default_bool: false +default_string: "415" +default_bytes: "416" +default_nested_enum: FOO +default_foreign_enum: FOREIGN_FOO +default_import_enum: IMPORT_FOO +default_string_piece: "424" +default_cord: "425" diff --git a/src/google/protobuf/testdata/text_format_unittest_extensions_data.txt b/src/google/protobuf/testdata/text_format_unittest_extensions_data.txt new file mode 100644 index 00000000..057beae8 --- /dev/null +++ b/src/google/protobuf/testdata/text_format_unittest_extensions_data.txt @@ -0,0 +1,116 @@ +[protobuf_unittest.optional_int32_extension]: 101 +[protobuf_unittest.optional_int64_extension]: 102 +[protobuf_unittest.optional_uint32_extension]: 103 +[protobuf_unittest.optional_uint64_extension]: 104 +[protobuf_unittest.optional_sint32_extension]: 105 +[protobuf_unittest.optional_sint64_extension]: 106 +[protobuf_unittest.optional_fixed32_extension]: 107 +[protobuf_unittest.optional_fixed64_extension]: 108 +[protobuf_unittest.optional_sfixed32_extension]: 109 +[protobuf_unittest.optional_sfixed64_extension]: 110 +[protobuf_unittest.optional_float_extension]: 111 +[protobuf_unittest.optional_double_extension]: 112 +[protobuf_unittest.optional_bool_extension]: true +[protobuf_unittest.optional_string_extension]: "115" +[protobuf_unittest.optional_bytes_extension]: "116" +[protobuf_unittest.optionalgroup_extension] { + a: 117 +} +[protobuf_unittest.optional_nested_message_extension] { + bb: 118 +} +[protobuf_unittest.optional_foreign_message_extension] { + c: 119 +} +[protobuf_unittest.optional_import_message_extension] { + d: 120 +} +[protobuf_unittest.optional_nested_enum_extension]: BAZ +[protobuf_unittest.optional_foreign_enum_extension]: FOREIGN_BAZ +[protobuf_unittest.optional_import_enum_extension]: IMPORT_BAZ +[protobuf_unittest.optional_string_piece_extension]: "124" +[protobuf_unittest.optional_cord_extension]: "125" +[protobuf_unittest.repeated_int32_extension]: 201 +[protobuf_unittest.repeated_int32_extension]: 301 +[protobuf_unittest.repeated_int64_extension]: 202 +[protobuf_unittest.repeated_int64_extension]: 302 +[protobuf_unittest.repeated_uint32_extension]: 203 +[protobuf_unittest.repeated_uint32_extension]: 303 +[protobuf_unittest.repeated_uint64_extension]: 204 +[protobuf_unittest.repeated_uint64_extension]: 304 +[protobuf_unittest.repeated_sint32_extension]: 205 +[protobuf_unittest.repeated_sint32_extension]: 305 +[protobuf_unittest.repeated_sint64_extension]: 206 +[protobuf_unittest.repeated_sint64_extension]: 306 +[protobuf_unittest.repeated_fixed32_extension]: 207 +[protobuf_unittest.repeated_fixed32_extension]: 307 +[protobuf_unittest.repeated_fixed64_extension]: 208 +[protobuf_unittest.repeated_fixed64_extension]: 308 +[protobuf_unittest.repeated_sfixed32_extension]: 209 +[protobuf_unittest.repeated_sfixed32_extension]: 309 +[protobuf_unittest.repeated_sfixed64_extension]: 210 +[protobuf_unittest.repeated_sfixed64_extension]: 310 +[protobuf_unittest.repeated_float_extension]: 211 +[protobuf_unittest.repeated_float_extension]: 311 +[protobuf_unittest.repeated_double_extension]: 212 +[protobuf_unittest.repeated_double_extension]: 312 +[protobuf_unittest.repeated_bool_extension]: true +[protobuf_unittest.repeated_bool_extension]: false +[protobuf_unittest.repeated_string_extension]: "215" +[protobuf_unittest.repeated_string_extension]: "315" +[protobuf_unittest.repeated_bytes_extension]: "216" +[protobuf_unittest.repeated_bytes_extension]: "316" +[protobuf_unittest.repeatedgroup_extension] { + a: 217 +} +[protobuf_unittest.repeatedgroup_extension] { + a: 317 +} +[protobuf_unittest.repeated_nested_message_extension] { + bb: 218 +} +[protobuf_unittest.repeated_nested_message_extension] { + bb: 318 +} +[protobuf_unittest.repeated_foreign_message_extension] { + c: 219 +} +[protobuf_unittest.repeated_foreign_message_extension] { + c: 319 +} +[protobuf_unittest.repeated_import_message_extension] { + d: 220 +} +[protobuf_unittest.repeated_import_message_extension] { + d: 320 +} +[protobuf_unittest.repeated_nested_enum_extension]: BAR +[protobuf_unittest.repeated_nested_enum_extension]: BAZ +[protobuf_unittest.repeated_foreign_enum_extension]: FOREIGN_BAR +[protobuf_unittest.repeated_foreign_enum_extension]: FOREIGN_BAZ +[protobuf_unittest.repeated_import_enum_extension]: IMPORT_BAR +[protobuf_unittest.repeated_import_enum_extension]: IMPORT_BAZ +[protobuf_unittest.repeated_string_piece_extension]: "224" +[protobuf_unittest.repeated_string_piece_extension]: "324" +[protobuf_unittest.repeated_cord_extension]: "225" +[protobuf_unittest.repeated_cord_extension]: "325" +[protobuf_unittest.default_int32_extension]: 401 +[protobuf_unittest.default_int64_extension]: 402 +[protobuf_unittest.default_uint32_extension]: 403 +[protobuf_unittest.default_uint64_extension]: 404 +[protobuf_unittest.default_sint32_extension]: 405 +[protobuf_unittest.default_sint64_extension]: 406 +[protobuf_unittest.default_fixed32_extension]: 407 +[protobuf_unittest.default_fixed64_extension]: 408 +[protobuf_unittest.default_sfixed32_extension]: 409 +[protobuf_unittest.default_sfixed64_extension]: 410 +[protobuf_unittest.default_float_extension]: 411 +[protobuf_unittest.default_double_extension]: 412 +[protobuf_unittest.default_bool_extension]: false +[protobuf_unittest.default_string_extension]: "415" +[protobuf_unittest.default_bytes_extension]: "416" +[protobuf_unittest.default_nested_enum_extension]: FOO +[protobuf_unittest.default_foreign_enum_extension]: FOREIGN_FOO +[protobuf_unittest.default_import_enum_extension]: IMPORT_FOO +[protobuf_unittest.default_string_piece_extension]: "424" +[protobuf_unittest.default_cord_extension]: "425" diff --git a/src/google/protobuf/testing/file.cc b/src/google/protobuf/testing/file.cc new file mode 100644 index 00000000..473d6919 --- /dev/null +++ b/src/google/protobuf/testing/file.cc @@ -0,0 +1,157 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/file/base/file.cc + +#include +#include +#include +#include +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN // yeah, right +#include // Find*File(). :( +#include +#include +#else +#include +#include +#endif +#include + +namespace google { +namespace protobuf { + +#ifdef _WIN32 +#define mkdir(name, mode) mkdir(name) +// Windows doesn't have symbolic links. +#define lstat stat +#ifndef F_OK +#define F_OK 00 // not defined by MSVC for whatever reason +#endif +#endif + +bool File::Exists(const string& name) { + return access(name.c_str(), F_OK) == 0; +} + +bool File::ReadFileToString(const string& name, string* output) { + char buffer[1024]; + FILE* file = fopen(name.c_str(), "rb"); + if (file == NULL) return false; + + while (true) { + size_t n = fread(buffer, 1, sizeof(buffer), file); + if (n <= 0) break; + output->append(buffer, n); + } + + int error = ferror(file); + if (fclose(file) != 0) return false; + return error == 0; +} + +void File::ReadFileToStringOrDie(const string& name, string* output) { + GOOGLE_CHECK(ReadFileToString(name, output)) << "Could not read: " << name; +} + +void File::WriteStringToFileOrDie(const string& contents, const string& name) { + FILE* file = fopen(name.c_str(), "wb"); + GOOGLE_CHECK(file != NULL); + GOOGLE_CHECK_EQ(fwrite(contents.data(), 1, contents.size(), file), + contents.size()); + GOOGLE_CHECK(fclose(file) == 0); +} + +bool File::CreateDir(const string& name, int mode) { + return mkdir(name.c_str(), mode) == 0; +} + +bool File::RecursivelyCreateDir(const string& path, int mode) { + if (CreateDir(path, mode)) return true; + + // Try creating the parent. + string::size_type slashpos = path.find_first_of('/'); + if (slashpos == string::npos) { + // No parent given. + return false; + } + + return RecursivelyCreateDir(path.substr(0, slashpos), mode) && + CreateDir(path, mode); +} + +void File::DeleteRecursively(const string& name, + void* dummy1, void* dummy2) { + // We don't care too much about error checking here since this is only used + // in tests to delete temporary directories that are under /tmp anyway. + +#ifdef _MSC_VER + // This interface is so weird. + WIN32_FIND_DATA find_data; + HANDLE find_handle = FindFirstFile((name + "/*").c_str(), &find_data); + if (find_handle == INVALID_HANDLE_VALUE) { + // Just delete it, whatever it is. + DeleteFile(name.c_str()); + RemoveDirectory(name.c_str()); + return; + } + + do { + string entry_name = find_data.cFileName; + if (entry_name != "." && entry_name != "..") { + string path = name + "/" + entry_name; + if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + DeleteRecursively(path, NULL, NULL); + RemoveDirectory(path.c_str()); + } else { + DeleteFile(path.c_str()); + } + } + } while(FindNextFile(find_handle, &find_data)); + FindClose(find_handle); + + RemoveDirectory(name.c_str()); +#else + // Use opendir()! Yay! + // lstat = Don't follow symbolic links. + struct stat stats; + if (lstat(name.c_str(), &stats) != 0) return; + + if (S_ISDIR(stats.st_mode)) { + DIR* dir = opendir(name.c_str()); + if (dir != NULL) { + while (true) { + struct dirent* entry = readdir(dir); + if (entry == NULL) break; + string entry_name = entry->d_name; + if (entry_name != "." && entry_name != "..") { + DeleteRecursively(name + "/" + entry_name, NULL, NULL); + } + } + } + + closedir(dir); + rmdir(name.c_str()); + + } else if (S_ISREG(stats.st_mode)) { + remove(name.c_str()); + } +#endif +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/testing/file.h b/src/google/protobuf/testing/file.h new file mode 100644 index 00000000..93335f1a --- /dev/null +++ b/src/google/protobuf/testing/file.h @@ -0,0 +1,69 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/file/base/file.h + +#ifndef GOOGLE_PROTOBUF_TESTING_FILE_H__ +#define GOOGLE_PROTOBUF_TESTING_FILE_H__ + +#include + +namespace google { +namespace protobuf { + +const int DEFAULT_FILE_MODE = 0777; + +// Protocol buffer code only uses a couple static methods of File, and only +// in tests. +class File { + public: + // Check if the file exists. + static bool Exists(const string& name); + + // Read an entire file to a string. Return true if successful, false + // otherwise. + static bool ReadFileToString(const string& name, string* output); + + // Same as above, but crash on failure. + static void ReadFileToStringOrDie(const string& name, string* output); + + // Create a file and write a string to it. + static void WriteStringToFileOrDie(const string& contents, + const string& name); + + // Create a directory. + static bool CreateDir(const string& name, int mode); + + // Create a directory and all parent directories if necessary. + static bool RecursivelyCreateDir(const string& path, int mode); + + // If "name" is a file, we delete it. If it is a directory, we + // call DeleteRecursively() for each file or directory (other than + // dot and double-dot) within it, and then delete the directory itself. + // The "dummy" parameters have a meaning in the original version of this + // method but they are not used anywhere in protocol buffers. + static void DeleteRecursively(const string& name, + void* dummy1, void* dummy2); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(File); +}; + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_TESTING_FILE_H__ diff --git a/src/google/protobuf/testing/googletest.cc b/src/google/protobuf/testing/googletest.cc new file mode 100644 index 00000000..aabc657f --- /dev/null +++ b/src/google/protobuf/testing/googletest.cc @@ -0,0 +1,189 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/testing/base/public/googletest.cc + +#include +#include +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#include +#include + +namespace google { +namespace protobuf { + +#ifdef _WIN32 +#define mkdir(name, mode) mkdir(name) +#endif + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +string TestSourceDir() { +#ifdef _MSC_VER + // Look for the "src" directory. + string prefix = "."; + + while (!File::Exists(prefix + "/src/google/protobuf")) { + if (!File::Exists(prefix)) { + GOOGLE_LOG(FATAL) + << "Could not find protobuf source code. Please run tests from " + "somewhere within the protobuf source package."; + } + prefix += "/.."; + } + return prefix + "/src"; +#else + // automake sets the "srcdir" environment variable. + char* result = getenv("srcdir"); + if (result == NULL) { + // Otherwise, the test must be run from the source directory. + return "."; + } else { + return result; + } +#endif +} + +namespace { + +string GetTemporaryDirectoryName() { + // tmpnam() is generally not considered safe but we're only using it for + // testing. We cannot use tmpfile() or mkstemp() since we're creating a + // directory. + string result = tmpnam(NULL); +#ifdef _WIN32 + // On Win32, tmpnam() returns a file prefixed with '\', but which is supposed + // to be used in the current working directory. WTF? + if (HasPrefixString(result, "\\")) { + result.erase(0, 1); + } +#endif // _WIN32 + return result; +} + +// Creates a temporary directory on demand and deletes it when the process +// quits. +class TempDirDeleter { + public: + TempDirDeleter() {} + ~TempDirDeleter() { + if (!name_.empty()) { + File::DeleteRecursively(name_, NULL, NULL); + } + } + + string GetTempDir() { + if (name_.empty()) { + name_ = GetTemporaryDirectoryName(); + GOOGLE_CHECK(mkdir(name_.c_str(), 0777) == 0) << strerror(errno); + + // Stick a file in the directory that tells people what this is, in case + // we abort and don't get a chance to delete it. + File::WriteStringToFileOrDie("", name_ + "/TEMP_DIR_FOR_PROTOBUF_TESTS"); + } + return name_; + } + + private: + string name_; +}; + +TempDirDeleter temp_dir_deleter_; + +} // namespace + +string TestTempDir() { + return temp_dir_deleter_.GetTempDir(); +} + +static string stderr_capture_filename_; +static int original_stderr_ = -1; + +void CaptureTestStderr() { + GOOGLE_CHECK_EQ(original_stderr_, -1) << "Already capturing."; + + stderr_capture_filename_ = TestTempDir() + "/captured_stderr"; + + int fd = open(stderr_capture_filename_.c_str(), + O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777); + GOOGLE_CHECK(fd >= 0) << "open: " << strerror(errno); + + original_stderr_ = dup(2); + close(2); + dup2(fd, 2); + close(fd); +} + +string GetCapturedTestStderr() { + GOOGLE_CHECK_NE(original_stderr_, -1) << "Not capturing."; + + close(2); + dup2(original_stderr_, 2); + original_stderr_ = -1; + + string result; + File::ReadFileToStringOrDie(stderr_capture_filename_, &result); + + remove(stderr_capture_filename_.c_str()); + + return result; +} + +ScopedMemoryLog* ScopedMemoryLog::active_log_ = NULL; + +ScopedMemoryLog::ScopedMemoryLog() { + GOOGLE_CHECK(active_log_ == NULL); + active_log_ = this; + old_handler_ = SetLogHandler(&HandleLog); +} + +ScopedMemoryLog::~ScopedMemoryLog() { + SetLogHandler(old_handler_); + active_log_ = NULL; +} + +const vector& ScopedMemoryLog::GetMessages(LogLevel dummy) const { + GOOGLE_CHECK_EQ(dummy, ERROR); + return messages_; +} + +void ScopedMemoryLog::HandleLog(LogLevel level, const char* filename, + int line, const string& message) { + GOOGLE_CHECK(active_log_ != NULL); + if (level == ERROR) { + active_log_->messages_.push_back(message); + } +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/testing/googletest.h b/src/google/protobuf/testing/googletest.h new file mode 100644 index 00000000..9420641a --- /dev/null +++ b/src/google/protobuf/testing/googletest.h @@ -0,0 +1,81 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/testing/base/public/googletest.h + +#ifndef GOOGLE_PROTOBUF_GOOGLETEST_H__ +#define GOOGLE_PROTOBUF_GOOGLETEST_H__ + +#include +#include + +namespace google { +namespace protobuf { + +// When running unittests, get the directory containing the source code. +string TestSourceDir(); + +// When running unittests, get a directory where temporary files may be +// placed. +string TestTempDir(); + +// Capture all text written to stderr. +void CaptureTestStderr(); + +// Stop capturing stderr and return the text captured. +string GetCapturedTestStderr(); + +// For use with ScopedMemoryLog::GetMessages(). Inside Google the LogLevel +// constants don't have the LOGLEVEL_ prefix, so the code that used +// ScopedMemoryLog refers to LOGLEVEL_ERROR as just ERROR. +static const LogLevel ERROR = LOGLEVEL_ERROR; + +// Receives copies of all LOG(ERROR) messages while in scope. Sample usage: +// { +// ScopedMemoryLog log; // constructor registers object as a log sink +// SomeRoutineThatMayLogMessages(); +// const vector& warnings = log.GetMessages(ERROR); +// } // destructor unregisters object as a log sink +// This is a dummy implementation which covers only what is used by protocol +// buffer unit tests. +class ScopedMemoryLog { + public: + ScopedMemoryLog(); + virtual ~ScopedMemoryLog(); + + // Fetches all messages logged. The internal version of this class + // would only fetch messages at the given security level, but the protobuf + // open source version ignores the argument since we always pass ERROR + // anyway. + const vector& GetMessages(LogLevel dummy) const; + + private: + vector messages_; + LogHandler* old_handler_; + + static void HandleLog(LogLevel level, const char* filename, int line, + const string& message); + + static ScopedMemoryLog* active_log_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ScopedMemoryLog); +}; + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_GOOGLETEST_H__ diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc new file mode 100644 index 00000000..63a64db1 --- /dev/null +++ b/src/google/protobuf/text_format.cc @@ -0,0 +1,941 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: jschorr@google.com (Joseph Schorr) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +string Message::DebugString() const { + string debug_string; + io::StringOutputStream output_stream(&debug_string); + + TextFormat::Print(*this, &output_stream); + + return debug_string; +} + +string Message::ShortDebugString() const { + // TODO(kenton): Make TextFormat support this natively instead of using + // DebugString() and munging the result. + string result = DebugString(); + + // Replace each contiguous range of whitespace (including newlines) with a + // single space. + for (int i = 0; i < result.size(); i++) { + int pos = i; + while (isspace(result[pos])) ++pos; + if (pos > i) result.replace(i, pos - i, " "); + } + + return result; +} + +void Message::PrintDebugString() const { + printf("%s", DebugString().c_str()); +} + +// =========================================================================== +// Internal class for parsing an ASCII representation of a Protocol Message. +// This class makes use of the Protocol Message compiler's tokenizer found +// in //google/protobuf/io/tokenizer.h. Note that class's Parse +// method is *not* thread-safe and should only be used in a single thread at +// a time. + +// Makes code slightly more readable. The meaning of "DO(foo)" is +// "Execute foo and fail if it fails.", where failure is indicated by +// returning false. Borrowed from parser.cc (Thanks Kenton!). +#define DO(STATEMENT) if (STATEMENT) {} else return false + +class TextFormat::ParserImpl { + public: + ParserImpl(io::ZeroCopyInputStream* input_stream, + io::ErrorCollector* error_collector) + : error_collector_(error_collector), + tokenizer_error_collector_(this), + tokenizer_(input_stream, &tokenizer_error_collector_), + root_message_type_(NULL) { + // For backwards-compatibility with proto1, we need to allow the 'f' suffix + // for floats. + tokenizer_.set_allow_f_after_float(true); + + // '#' starts a comment. + tokenizer_.set_comment_style(io::Tokenizer::SH_COMMENT_STYLE); + + // Consume the starting token. + tokenizer_.Next(); + } + ~ParserImpl() { } + + // Parses the ASCII representation specified in input and saves the + // information into the output pointer (a Message). Returns + // false if an error occurs (an error will also be logged to + // GOOGLE_LOG(ERROR)). + bool Parse(Message* output) { + Message::Reflection* reflection = output->GetReflection(); + const Descriptor* descriptor = output->GetDescriptor(); + root_message_type_ = descriptor; + + // Consume fields until we cannot do so anymore. + while(true) { + if (LookingAtType(io::Tokenizer::TYPE_END)) { + return true; + } + + DO(ConsumeField(reflection, descriptor)); + } + } + + void ReportError(int line, int col, const string& message) { + if (error_collector_ == NULL) { + if (line >= 0) { + GOOGLE_LOG(ERROR) << "Error parsing text-format " + << root_message_type_->full_name() + << ": " << (line + 1) << ":" + << (col + 1) << ": " << message; + } else { + GOOGLE_LOG(ERROR) << "Error parsing text-format " + << root_message_type_->full_name() + << ": " << message; + } + } else { + error_collector_->AddError(line, col, message); + } + } + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ParserImpl); + + // Reports an error with the given message with information indicating + // the position (as derived from the current token). + void ReportError(const string& message) { + ReportError(tokenizer_.current().line, tokenizer_.current().column, + message); + } + + // Consumes the specified message with the given starting delimeter. + // This method checks to see that the end delimeter at the conclusion of + // the consumption matches the starting delimeter passed in here. + bool ConsumeMessage(Message* message, const string delimeter) { + Message::Reflection* reflection = message->GetReflection(); + const Descriptor* descriptor = message->GetDescriptor(); + + while (!LookingAt(">") && !LookingAt("}")) { + DO(ConsumeField(reflection, descriptor)); + } + + // Confirm that we have a valid ending delimeter. + DO(Consume(delimeter)); + + return true; + } + + // Consumes the current field (as returned by the tokenizer) on the + // passed in message. + bool ConsumeField(Message::Reflection* reflection, + const Descriptor* descriptor) { + string field_name; + + const FieldDescriptor* field = NULL; + + if (TryConsume("[")) { + // Extension. + DO(ConsumeIdentifier(&field_name)); + while (TryConsume(".")) { + string part; + DO(ConsumeIdentifier(&part)); + field_name += "."; + field_name += part; + } + DO(Consume("]")); + + field = reflection->FindKnownExtensionByName(field_name); + + if (field == NULL) { + ReportError("Extension \"" + field_name + "\" is not defined or " + "is not an extension of \"" + + descriptor->full_name() + "\"."); + return false; + } + } else { + DO(ConsumeIdentifier(&field_name)); + + field = descriptor->FindFieldByName(field_name); + // Group names are expected to be capitalized as they appear in the + // .proto file, which actually matches their type names, not their field + // names. + if (field == NULL) { + string lower_field_name = field_name; + LowerString(&lower_field_name); + field = descriptor->FindFieldByName(lower_field_name); + // If the case-insensitive match worked but the field is NOT a group, + if (field != NULL && field->type() != FieldDescriptor::TYPE_GROUP) { + field = NULL; + } + } + // Again, special-case group names as described above. + if (field != NULL && field->type() == FieldDescriptor::TYPE_GROUP + && field->message_type()->name() != field_name) { + field = NULL; + } + + if (field == NULL) { + ReportError("Message type \"" + descriptor->full_name() + + "\" has no field named \"" + field_name + "\"."); + return false; + } + } + + // Perform special handling for embedded message types. + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + string delimeter; + + // ':' is optional here. + TryConsume(":"); + + if (TryConsume("<")) { + delimeter = ">"; + } else { + DO(Consume("{")); + delimeter = "}"; + } + + if (field->is_repeated()) { + DO(ConsumeMessage(reflection->AddMessage(field), delimeter)); + } else { + DO(ConsumeMessage(reflection->MutableMessage(field), delimeter)); + } + } else { + DO(Consume(":")); + DO(ConsumeFieldValue(reflection, field)); + } + + return true; + } + + bool ConsumeFieldValue(Message::Reflection* reflection, + const FieldDescriptor* field) { + +// Define an easy to use macro for setting fields. This macro checks +// to see if the field is repeated (in which case we need to use the Add +// methods or not (in which case we need to use the Set methods). +#define SET_FIELD(CPPTYPE, VALUE) \ + if (field->is_repeated()) { \ + reflection->Add##CPPTYPE(field, VALUE); \ + } else { \ + reflection->Set##CPPTYPE(field, VALUE); \ + } \ + + switch(field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: { + int64 value; + DO(ConsumeSignedInteger(&value, kint32max)); + SET_FIELD(Int32, static_cast(value)); + break; + } + + case FieldDescriptor::CPPTYPE_UINT32: { + uint64 value; + DO(ConsumeUnsignedInteger(&value, kuint32max)); + SET_FIELD(UInt32, static_cast(value)); + break; + } + + case FieldDescriptor::CPPTYPE_INT64: { + int64 value; + DO(ConsumeSignedInteger(&value, kint64max)); + SET_FIELD(Int64, value); + break; + } + + case FieldDescriptor::CPPTYPE_UINT64: { + uint64 value; + DO(ConsumeUnsignedInteger(&value, kuint64max)); + SET_FIELD(UInt64, value); + break; + } + + case FieldDescriptor::CPPTYPE_FLOAT: { + double value; + DO(ConsumeDouble(&value)); + SET_FIELD(Float, static_cast(value)); + break; + } + + case FieldDescriptor::CPPTYPE_DOUBLE: { + double value; + DO(ConsumeDouble(&value)); + SET_FIELD(Double, value); + break; + } + + case FieldDescriptor::CPPTYPE_STRING: { + string value; + DO(ConsumeString(&value)); + SET_FIELD(String, value); + break; + } + + case FieldDescriptor::CPPTYPE_BOOL: { + string value; + DO(ConsumeIdentifier(&value)); + + if (value == "true") { + SET_FIELD(Bool, true); + } else if (value == "false") { + SET_FIELD(Bool, false); + } else { + ReportError("Invalid value for boolean field \"" + field->name() + + "\". Value: \"" + value + "\"."); + return false; + } + break; + } + + case FieldDescriptor::CPPTYPE_ENUM: { + string value; + DO(ConsumeIdentifier(&value)); + + // Find the enumeration value. + const EnumDescriptor* enum_type = field->enum_type(); + const EnumValueDescriptor* enum_value + = enum_type->FindValueByName(value); + + if (enum_value == NULL) { + ReportError("Unknown enumeration value of \"" + value + "\" for " + "field \"" + field->name() + "\"."); + return false; + } + + SET_FIELD(Enum, enum_value); + break; + } + + case FieldDescriptor::CPPTYPE_MESSAGE: { + // We should never get here. Put here instead of a default + // so that if new types are added, we get a nice compiler warning. + GOOGLE_LOG(FATAL) << "Reached an unintended state: CPPTYPE_MESSAGE"; + break; + } + } +#undef SET_FIELD + return true; + } + + // Returns true if the current token's text is equal to that specified. + bool LookingAt(const string& text) { + return tokenizer_.current().text == text; + } + + // Returns true if the current token's type is equal to that specified. + bool LookingAtType(io::Tokenizer::TokenType token_type) { + return tokenizer_.current().type == token_type; + } + + // Consumes an identifier and saves its value in the identifier parameter. + // Returns false if the token is not of type IDENTFIER. + bool ConsumeIdentifier(string* identifier) { + if (!LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + ReportError("Expected identifier."); + return false; + } + + *identifier = tokenizer_.current().text; + + tokenizer_.Next(); + return true; + } + + // Consumes a string and saves its value in the text parameter. + // Returns false if the token is not of type STRING. + bool ConsumeString(string* text) { + if (!LookingAtType(io::Tokenizer::TYPE_STRING)) { + ReportError("Expected string."); + return false; + } + + io::Tokenizer::ParseString(tokenizer_.current().text, text); + + tokenizer_.Next(); + return true; + } + + // Consumes a uint64 and saves its value in the value parameter. + // Returns false if the token is not of type INTEGER. + bool ConsumeUnsignedInteger(uint64* value, uint64 max_value) { + if (!LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + ReportError("Expected integer."); + return false; + } + + if (!io::Tokenizer::ParseInteger(tokenizer_.current().text, + max_value, value)) { + ReportError("Integer out of range."); + return false; + } + + tokenizer_.Next(); + return true; + } + + // Consumes an int64 and saves its value in the value parameter. + // Note that since the tokenizer does not support negative numbers, + // we actually may consume an additional token (for the minus sign) in this + // method. Returns false if the token is not an integer + // (signed or otherwise). + bool ConsumeSignedInteger(int64* value, uint64 max_value) { + bool negative = false; + + if (TryConsume("-")) { + negative = true; + // Two's complement always allows one more negative integer than + // positive. + ++max_value; + } + + uint64 unsigned_value; + + DO(ConsumeUnsignedInteger(&unsigned_value, max_value)); + + *value = static_cast(unsigned_value); + + if (negative) { + *value = -*value; + } + + return true; + } + + // Consumes a double and saves its value in the value parameter. + // Note that since the tokenizer does not support negative numbers, + // we actually may consume an additional token (for the minus sign) in this + // method. Returns false if the token is not a double + // (signed or otherwise). + bool ConsumeDouble(double* value) { + bool negative = false; + + if (TryConsume("-")) { + negative = true; + } + + // A double can actually be an integer, according to the tokenizer. + // Therefore, we must check both cases here. + if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + // We have found an integer value for the double. + uint64 integer_value; + DO(ConsumeUnsignedInteger(&integer_value, kuint64max)); + + *value = static_cast(integer_value); + } else if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) { + // We have found a float value for the double. + *value = io::Tokenizer::ParseFloat(tokenizer_.current().text); + + // Mark the current token as consumed. + tokenizer_.Next(); + } else if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + string text = tokenizer_.current().text; + LowerString(&text); + if (text == "inf" || text == "infinity") { + *value = std::numeric_limits::infinity(); + tokenizer_.Next(); + } else if (text == "nan") { + *value = std::numeric_limits::quiet_NaN(); + tokenizer_.Next(); + } else { + ReportError("Expected double."); + return false; + } + } else { + ReportError("Expected double."); + return false; + } + + if (negative) { + *value = -*value; + } + + return true; + } + + // Consumes a token and confirms that it matches that specified in the + // value parameter. Returns false if the token found does not match that + // which was specified. + bool Consume(const string& value) { + const string& current_value = tokenizer_.current().text; + + if (current_value != value) { + ReportError("Expected \"" + value + "\", found \"" + current_value + + "\"."); + return false; + } + + tokenizer_.Next(); + + return true; + } + + // Attempts to consume the supplied value. Returns false if a the + // token found does not match the value specified. + bool TryConsume(const string& value) { + if (tokenizer_.current().text == value) { + tokenizer_.Next(); + return true; + } else { + return false; + } + } + + // An internal instance of the Tokenizer's error collector, used to + // collect any base-level parse errors and feed them to the ParserImpl. + class ParserErrorCollector : public io::ErrorCollector { + public: + explicit ParserErrorCollector(TextFormat::ParserImpl* parser) : + parser_(parser) { } + + virtual ~ParserErrorCollector() { }; + + virtual void AddError(int line, int column, const string& message) { + parser_->ReportError(line, column, message); + } + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ParserErrorCollector); + TextFormat::ParserImpl* parser_; + }; + + io::ErrorCollector* error_collector_; + ParserErrorCollector tokenizer_error_collector_; + io::Tokenizer tokenizer_; + const Descriptor* root_message_type_; +}; + +#undef DO + +// =========================================================================== +// Internal class for writing text to the io::ZeroCopyOutputStream. Adapted +// from the Printer found in //google/protobuf/io/printer.h +class TextFormat::TextGenerator { + public: + explicit TextGenerator(io::ZeroCopyOutputStream* output) + : output_(output), + buffer_(NULL), + buffer_size_(0), + at_start_of_line_(true), + failed_(false), + indent_("") { + } + + ~TextGenerator() { + // Only BackUp() if we're sure we've successfully called Next() at least + // once. + if (buffer_size_ > 0) { + output_->BackUp(buffer_size_); + } + } + + // Indent text by two spaces. After calling Indent(), two spaces will be + // inserted at the beginning of each line of text. Indent() may be called + // multiple times to produce deeper indents. + void Indent() { + indent_ += " "; + } + + // Reduces the current indent level by two spaces, or crashes if the indent + // level is zero. + void Outdent() { + if (indent_.empty()) { + GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent()."; + return; + } + + indent_.resize(indent_.size() - 2); + } + + // Print text to the output stream. + void Print(const string& str) { + Print(str.c_str()); + } + + // Print text to the output stream. + void Print(const char* text) { + int size = strlen(text); + int pos = 0; // The number of bytes we've written so far. + + for (int i = 0; i < size; i++) { + if (text[i] == '\n') { + // Saw newline. If there is more text, we may need to insert an indent + // here. So, write what we have so far, including the '\n'. + Write(text + pos, i - pos + 1); + pos = i + 1; + + // Setting this true will cause the next Write() to insert an indent + // first. + at_start_of_line_ = true; + } + } + + // Write the rest. + Write(text + pos, size - pos); + } + + // True if any write to the underlying stream failed. (We don't just + // crash in this case because this is an I/O failure, not a programming + // error.) + bool failed() const { return failed_; } + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextGenerator); + + void Write(const char* data, int size) { + if (failed_) return; + if (size == 0) return; + + if (at_start_of_line_) { + // Insert an indent. + at_start_of_line_ = false; + Write(indent_.data(), indent_.size()); + if (failed_) return; + } + + while (size > buffer_size_) { + // Data exceeds space in the buffer. Copy what we can and request a + // new buffer. + memcpy(buffer_, data, buffer_size_); + data += buffer_size_; + size -= buffer_size_; + void* void_buffer; + failed_ = !output_->Next(&void_buffer, &buffer_size_); + if (failed_) return; + buffer_ = reinterpret_cast(void_buffer); + } + + // Buffer is big enough to receive the data; copy it. + memcpy(buffer_, data, size); + buffer_ += size; + buffer_size_ -= size; + } + + io::ZeroCopyOutputStream* const output_; + char* buffer_; + int buffer_size_; + bool at_start_of_line_; + bool failed_; + + string indent_; +}; + +// =========================================================================== + +TextFormat::Parser::Parser() + : error_collector_(NULL), + allow_partial_(false) {} + +TextFormat::Parser::~Parser() {} + +bool TextFormat::Parser::Parse(io::ZeroCopyInputStream* input, + Message* output) { + output->Clear(); + return Merge(input, output); +} + +bool TextFormat::Parser::ParseFromString(const string& input, + Message* output) { + io::ArrayInputStream input_stream(input.data(), input.size()); + return Parse(&input_stream, output); +} + +bool TextFormat::Parser::Merge(io::ZeroCopyInputStream* input, + Message* output) { + ParserImpl parser(input, error_collector_); + if (!parser.Parse(output)) return false; + if (!allow_partial_ && !output->IsInitialized()) { + vector missing_fields; + output->FindInitializationErrors(&missing_fields); + parser.ReportError(-1, 0, "Message missing required fields: " + + JoinStrings(missing_fields, ", ")); + return false; + } + return true; +} + +bool TextFormat::Parser::MergeFromString(const string& input, + Message* output) { + io::ArrayInputStream input_stream(input.data(), input.size()); + return Merge(&input_stream, output); +} + + +/* static */ bool TextFormat::Parse(io::ZeroCopyInputStream* input, + Message* output) { + return Parser().Parse(input, output); +} + +/* static */ bool TextFormat::Merge(io::ZeroCopyInputStream* input, + Message* output) { + return Parser().Merge(input, output); +} + +/* static */ bool TextFormat::ParseFromString(const string& input, + Message* output) { + return Parser().ParseFromString(input, output); +} + +/* static */ bool TextFormat::MergeFromString(const string& input, + Message* output) { + return Parser().MergeFromString(input, output); +} + +/* static */ bool TextFormat::PrintToString(const Message& message, + string* output) { + GOOGLE_DCHECK(output) << "output specified is NULL"; + + output->clear(); + io::StringOutputStream output_stream(output); + + bool result = Print(message, &output_stream); + + return result; +} + +/* static */ bool TextFormat::Print(const Message& message, + io::ZeroCopyOutputStream* output) { + TextGenerator generator(output); + + Print(message.GetDescriptor(), message.GetReflection(), generator); + + // Output false if the generator failed internally. + return !generator.failed(); +} + +/* static */ void TextFormat::Print(const Descriptor* descriptor, + const Message::Reflection* message, + TextGenerator& generator) { + vector fields; + message->ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + PrintField(fields[i], message, generator); + } + PrintUnknownFields(message->GetUnknownFields(), generator); +} + +/* static */ void TextFormat::PrintFieldValueToString( + const Message& message, + const FieldDescriptor* field, + int index, + string* output) { + + GOOGLE_DCHECK(output) << "output specified is NULL"; + + output->clear(); + io::StringOutputStream output_stream(output); + TextGenerator generator(&output_stream); + + PrintFieldValue(message.GetReflection(), field, index, generator); +} + +/* static */ void TextFormat::PrintField(const FieldDescriptor* field, + const Message::Reflection* message, + TextGenerator& generator) { + int count = 0; + + if (field->is_repeated()) { + count = message->FieldSize(field); + } else if (message->HasField(field)) { + count = 1; + } + + for (int j = 0; j < count; ++j) { + if (field->is_extension()) { + generator.Print("["); + // We special-case MessageSet elements for compatibility with proto1. + if (field->containing_type()->options().message_set_wire_format() + && field->type() == FieldDescriptor::TYPE_MESSAGE + && field->is_optional() + && field->extension_scope() == field->message_type()) { + generator.Print(field->message_type()->full_name()); + } else { + generator.Print(field->full_name()); + } + generator.Print("]"); + } else { + if (field->type() == FieldDescriptor::TYPE_GROUP) { + // Groups must be serialized with their original capitalization. + generator.Print(field->message_type()->name()); + } else { + generator.Print(field->name()); + } + } + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + generator.Print(" {\n"); + generator.Indent(); + } else { + generator.Print(": "); + } + + // Write the field value. + int field_index = j; + if (!field->is_repeated()) { + field_index = -1; + } + + PrintFieldValue(message, field, field_index, generator); + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + generator.Outdent(); + generator.Print("}"); + } + + generator.Print("\n"); + } +} + +/* static */ void TextFormat::PrintFieldValue( + const Message::Reflection* reflection, + const FieldDescriptor* field, + int index, + TextGenerator& generator) { + GOOGLE_DCHECK(field->is_repeated() || (index == -1)) + << "Index must be -1 for non-repeated fields"; + + switch (field->cpp_type()) { +#define OUTPUT_FIELD(CPPTYPE, METHOD, TO_STRING) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + generator.Print(TO_STRING(field->is_repeated() ? \ + reflection->GetRepeated##METHOD(field, index) : \ + reflection->Get##METHOD(field))); \ + break; \ + + OUTPUT_FIELD( INT32, Int32, SimpleItoa); + OUTPUT_FIELD( INT64, Int64, SimpleItoa); + OUTPUT_FIELD(UINT32, UInt32, SimpleItoa); + OUTPUT_FIELD(UINT64, UInt64, SimpleItoa); + OUTPUT_FIELD( FLOAT, Float, SimpleFtoa); + OUTPUT_FIELD(DOUBLE, Double, SimpleDtoa); +#undef OUTPUT_FIELD + + case FieldDescriptor::CPPTYPE_STRING: { + string scratch; + const string& value = field->is_repeated() ? + reflection->GetRepeatedStringReference(field, index, &scratch) : + reflection->GetStringReference(field, &scratch); + + generator.Print("\""); + generator.Print(CEscape(value)); + generator.Print("\""); + + break; + } + + case FieldDescriptor::CPPTYPE_BOOL: + if (field->is_repeated()) { + generator.Print(reflection->GetRepeatedBool(field, index) + ? "true" : "false"); + } else { + generator.Print(reflection->GetBool(field) ? "true" : "false"); + } + break; + + case FieldDescriptor::CPPTYPE_ENUM: + generator.Print(field->is_repeated() ? + reflection->GetRepeatedEnum(field, index)->name() + : reflection->GetEnum(field)->name()); + break; + + case FieldDescriptor::CPPTYPE_MESSAGE: + Print(field->message_type(), + field->is_repeated() ? + reflection->GetRepeatedMessage(field, index).GetReflection() + : reflection->GetMessage(field).GetReflection(), generator); + break; + } +} + +// Prints an integer as hex with a fixed number of digits dependent on the +// integer type. +template +static string PaddedHex(IntType value) { + string result; + result.reserve(sizeof(value) * 2); + for (int i = sizeof(value) * 2 - 1; i >= 0; i--) { + result.push_back(int_to_hex_digit(value >> (i*4) & 0x0F)); + } + return result; +} + +/* static */ void TextFormat::PrintUnknownFields( + const UnknownFieldSet& unknown_fields, TextGenerator& generator) { + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + string field_number = SimpleItoa(field.number()); + + for (int j = 0; j < field.varint_size(); j++) { + generator.Print(field_number); + generator.Print(": "); + generator.Print(SimpleItoa(field.varint(j))); + generator.Print("\n"); + } + for (int j = 0; j < field.fixed32_size(); j++) { + generator.Print(field_number); + generator.Print(": 0x"); + char buffer[kFastToBufferSize]; + generator.Print(FastHex32ToBuffer(field.fixed32(j), buffer)); + generator.Print("\n"); + } + for (int j = 0; j < field.fixed64_size(); j++) { + generator.Print(field_number); + generator.Print(": 0x"); + char buffer[kFastToBufferSize]; + generator.Print(FastHex64ToBuffer(field.fixed64(j), buffer)); + generator.Print("\n"); + } + for (int j = 0; j < field.length_delimited_size(); j++) { + generator.Print(field_number); + generator.Print(": \""); + generator.Print(CEscape(field.length_delimited(j))); + generator.Print("\"\n"); + } + for (int j = 0; j < field.group_size(); j++) { + generator.Print(field_number); + generator.Print(" {\n"); + generator.Indent(); + PrintUnknownFields(field.group(j), generator); + generator.Outdent(); + generator.Print("}\n"); + } + } +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h new file mode 100644 index 00000000..df27710d --- /dev/null +++ b/src/google/protobuf/text_format.h @@ -0,0 +1,143 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: jschorr@google.com (Joseph Schorr) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Utilities for printing and parsing protocol messages in a human-readable, +// text-based format. + +#ifndef GOOGLE_PROTOBUF_TEXT_FORMAT_H__ +#define GOOGLE_PROTOBUF_TEXT_FORMAT_H__ + +#include +#include // Message, Message::Reflection +#include + +namespace google { +namespace protobuf { + +namespace io { + class ErrorCollector; // tokenizer.h +} + +// This class implements protocol buffer text format. Printing and parsing +// protocol messages in text format is useful for debugging and human editing +// of messages. +// +// This class is really a namespace that contains only static methods. +class LIBPROTOBUF_EXPORT TextFormat { + public: + // Outputs a textual representation of the given message to the given + // output stream. + static bool Print(const Message& message, io::ZeroCopyOutputStream* output); + // Like Print(), but outputs directly to a string. + static bool PrintToString(const Message& message, string* output); + + // Outputs a textual representation of the value of the field supplied on + // the message supplied. For non-repeated fields, an index of -1 must + // be supplied. Note that this method will print the default value for a + // field if it is not set. + static void PrintFieldValueToString(const Message& message, + const FieldDescriptor* field, + int index, + string* output); + + // Parses a text-format protocol message from the given input stream to + // the given message object. This function parses the format written + // by Print(). + static bool Parse(io::ZeroCopyInputStream* input, Message* output); + // Like Parse(), but reads directly from a string. + static bool ParseFromString(const string& input, Message* output); + + // Like Parse(), but the data is merged into the given message, as if + // using Message::MergeFrom(). + static bool Merge(io::ZeroCopyInputStream* input, Message* output); + // Like Merge(), but reads directly from a string. + static bool MergeFromString(const string& input, Message* output); + + // For more control over parsing, use this class. + class LIBPROTOBUF_EXPORT Parser { + public: + Parser(); + ~Parser(); + + // Like TextFormat::Parse(). + bool Parse(io::ZeroCopyInputStream* input, Message* output); + // Like TextFormat::ParseFromString(). + bool ParseFromString(const string& input, Message* output); + // Like TextFormat::Merge(). + bool Merge(io::ZeroCopyInputStream* input, Message* output); + // Like TextFormat::MergeFromString(). + bool MergeFromString(const string& input, Message* output); + + // Set where to report parse errors. If NULL (the default), errors will + // be printed to stderr. + void RecordErrorsTo(io::ErrorCollector* error_collector) { + error_collector_ = error_collector; + } + + // Normally parsing fails if, after parsing, output->IsInitialized() + // returns false. Call AllowPartialMessage(true) to skip this check. + void AllowPartialMessage(bool allow) { + allow_partial_ = allow; + } + + private: + io::ErrorCollector* error_collector_; + bool allow_partial_; + }; + + private: + // Forward declaration of an internal class used to print the text + // output to the OutputStream (see text_format.cc for implementation). + class TextGenerator; + + // Forward declaration of an internal class used to parse text + // representations (see text_format.cc for implementation). + class ParserImpl; + + // Internal Print method, used for writing to the OutputStream via + // the TextGenerator class. + static void Print(const Descriptor* descriptor, + const Message::Reflection* message, + TextGenerator& generator); + + // Print a single field. + static void PrintField(const FieldDescriptor* field, + const Message::Reflection* message, + TextGenerator& generator); + + // Outputs a textual representation of the value of the field supplied on + // the message supplied or the default value if not set. + static void PrintFieldValue(const Message::Reflection* reflection, + const FieldDescriptor* field, + int index, + TextGenerator& generator); + + // Print the fields in an UnknownFieldSet. They are printed by tag number + // only. + static void PrintUnknownFields(const UnknownFieldSet& unknown_fields, + TextGenerator& generator); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextFormat); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_TEXT_FORMAT_H__ diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc new file mode 100644 index 00000000..48c70763 --- /dev/null +++ b/src/google/protobuf/text_format_unittest.cc @@ -0,0 +1,697 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: jschorr@google.com (Joseph Schorr) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace { + +inline bool IsNaN(double value) { + // NaN is never equal to anything, even itself. + return value != value; +} + +// A basic string with different escapable characters for testing. +const string kEscapeTestString = + "\"A string with ' characters \n and \r newlines and \t tabs and \001 " + "slashes \\"; + +// A representation of the above string with all the characters escaped. +const string kEscapeTestStringEscaped = + "\"\\\"A string with \\' characters \\n and \\r newlines " + "and \\t tabs and \\001 slashes \\\\\""; + +class TextFormatTest : public testing::Test { + public: + static void SetUpTestCase() { + File::ReadFileToStringOrDie( + TestSourceDir() + + "/google/protobuf/testdata/text_format_unittest_data.txt", + &static_proto_debug_string_); + } + + TextFormatTest() : proto_debug_string_(static_proto_debug_string_) {} + + protected: + // Debug string read from text_format_unittest_data.txt. + const string proto_debug_string_; + unittest::TestAllTypes proto_; + + private: + static string static_proto_debug_string_; +}; +string TextFormatTest::static_proto_debug_string_; + +class TextFormatExtensionsTest : public testing::Test { + public: + static void SetUpTestCase() { + File::ReadFileToStringOrDie( + TestSourceDir() + + "/google/protobuf/testdata/" + "text_format_unittest_extensions_data.txt", + &static_proto_debug_string_); + } + + TextFormatExtensionsTest() + : proto_debug_string_(static_proto_debug_string_) {} + + protected: + // Debug string read from text_format_unittest_data.txt. + const string proto_debug_string_; + unittest::TestAllExtensions proto_; + + private: + static string static_proto_debug_string_; +}; +string TextFormatExtensionsTest::static_proto_debug_string_; + + +TEST_F(TextFormatTest, Basic) { + TestUtil::SetAllFields(&proto_); + EXPECT_EQ(proto_debug_string_, proto_.DebugString()); +} + +TEST_F(TextFormatExtensionsTest, Extensions) { + TestUtil::SetAllExtensions(&proto_); + EXPECT_EQ(proto_debug_string_, proto_.DebugString()); +} + +TEST_F(TextFormatTest, StringEscape) { + // Set the string value to test. + proto_.set_optional_string(kEscapeTestString); + + // Get the DebugString from the proto. + string debug_string = proto_.DebugString(); + + // Hardcode a correct value to test against. + string correct_string = "optional_string: " + + kEscapeTestStringEscaped + + "\n"; + + // Compare. + EXPECT_EQ(correct_string, debug_string); +} + +TEST_F(TextFormatTest, PrintUnknownFields) { + // Test printing of unknown fields in a message. + + unittest::TestEmptyMessage message; + UnknownFieldSet* unknown_fields = message.mutable_unknown_fields(); + UnknownField* field5 = unknown_fields->AddField(5); + + field5->add_varint(1); + field5->add_fixed32(2); + field5->add_fixed64(3); + field5->add_length_delimited("4"); + field5->add_group()->AddField(10)->add_varint(5); + + UnknownField* field8 = unknown_fields->AddField(8); + field8->add_varint(1); + field8->add_varint(2); + field8->add_varint(3); + + EXPECT_EQ( + "5: 1\n" + "5: 0x00000002\n" + "5: 0x0000000000000003\n" + "5: \"4\"\n" + "5 {\n" + " 10: 5\n" + "}\n" + "8: 1\n" + "8: 2\n" + "8: 3\n", + message.DebugString()); +} + +TEST_F(TextFormatTest, ParseBasic) { + io::ArrayInputStream input_stream(proto_debug_string_.data(), + proto_debug_string_.size()); + TextFormat::Parse(&input_stream, &proto_); + TestUtil::ExpectAllFieldsSet(proto_); +} + +TEST_F(TextFormatExtensionsTest, ParseExtensions) { + io::ArrayInputStream input_stream(proto_debug_string_.data(), + proto_debug_string_.size()); + TextFormat::Parse(&input_stream, &proto_); + TestUtil::ExpectAllExtensionsSet(proto_); +} + +TEST_F(TextFormatTest, ParseStringEscape) { + // Create a parse string with escpaed characters in it. + string parse_string = "optional_string: " + + kEscapeTestStringEscaped + + "\n"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_EQ(kEscapeTestString, proto_.optional_string()); +} + +TEST_F(TextFormatTest, ParseFloatWithSuffix) { + // Test that we can parse a floating-point value with 'f' appended to the + // end. This is needed for backwards-compatibility with proto1. + + // Have it parse a float with the 'f' suffix. + string parse_string = "optional_float: 1.0f\n"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_EQ(1.0, proto_.optional_float()); +} + +TEST_F(TextFormatTest, Comments) { + // Test that comments are ignored. + + string parse_string = "optional_int32: 1 # a comment\n" + "optional_int64: 2 # another comment"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_EQ(1, proto_.optional_int32()); + EXPECT_EQ(2, proto_.optional_int64()); +} + +TEST_F(TextFormatTest, OptionalColon) { + // Test that we can place a ':' after the field name of a nested message, + // even though we don't have to. + + string parse_string = "optional_nested_message: { bb: 1}\n"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_TRUE(proto_.has_optional_nested_message()); + EXPECT_EQ(1, proto_.optional_nested_message().bb()); +} + +// Some platforms (e.g. Windows) insist on padding the exponent to three +// digits when one or two would be just fine. +static string RemoveRedundantZeros(string text) { + text = StringReplace(text, "e+0", "e+", true); + text = StringReplace(text, "e-0", "e-", true); + return text; +} + +TEST_F(TextFormatTest, PrintExotic) { + unittest::TestAllTypes message; + + // Note: In C, a negative integer literal is actually the unary negation + // operator being applied to a positive integer literal, and + // 9223372036854775808 is outside the range of int64. However, it is not + // outside the range of uint64. Confusingly, this means that everything + // works if we make the literal unsigned, even though we are negating it. + message.add_repeated_int64(-GOOGLE_ULONGLONG(9223372036854775808)); + message.add_repeated_uint64(GOOGLE_ULONGLONG(18446744073709551615)); + message.add_repeated_double(123.456); + message.add_repeated_double(1.23e21); + message.add_repeated_double(1.23e-18); + message.add_repeated_double(std::numeric_limits::infinity()); + message.add_repeated_double(-std::numeric_limits::infinity()); + message.add_repeated_double(std::numeric_limits::quiet_NaN()); + message.add_repeated_string(string("\000\001\a\b\f\n\r\t\v\\\'\"", 12)); + + // Fun story: We used to use 1.23e22 instead of 1.23e21 above, but this + // seemed to trigger an odd case on MinGW/GCC 3.4.5 where GCC's parsing of + // the value differed from strtod()'s parsing. That is to say, the + // following assertion fails on MinGW: + // assert(1.23e22 == strtod("1.23e22", NULL)); + // As a result, SimpleDtoa() would print the value as + // "1.2300000000000001e+22" to make sure strtod() produce the exact same + // result. Our goal is to test runtime parsing, not compile-time parsing, + // so this wasn't our problem. It was found that using 1.23e21 did not + // have this problem, so we switched to that instead. + + EXPECT_EQ( + "repeated_int64: -9223372036854775808\n" + "repeated_uint64: 18446744073709551615\n" + "repeated_double: 123.456\n" + "repeated_double: 1.23e+21\n" + "repeated_double: 1.23e-18\n" + "repeated_double: inf\n" + "repeated_double: -inf\n" + "repeated_double: nan\n" + "repeated_string: \"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\'\\\"\"\n", + RemoveRedundantZeros(message.DebugString())); +} + +TEST_F(TextFormatTest, PrintFloatPrecision) { + unittest::TestAllTypes message; + + message.add_repeated_float(1.2); + message.add_repeated_float(1.23); + message.add_repeated_float(1.234); + message.add_repeated_float(1.2345); + message.add_repeated_float(1.23456); + message.add_repeated_float(1.2e10); + message.add_repeated_float(1.23e10); + message.add_repeated_float(1.234e10); + message.add_repeated_float(1.2345e10); + message.add_repeated_float(1.23456e10); + message.add_repeated_double(1.2); + message.add_repeated_double(1.23); + message.add_repeated_double(1.234); + message.add_repeated_double(1.2345); + message.add_repeated_double(1.23456); + message.add_repeated_double(1.234567); + message.add_repeated_double(1.2345678); + message.add_repeated_double(1.23456789); + message.add_repeated_double(1.234567898); + message.add_repeated_double(1.2345678987); + message.add_repeated_double(1.23456789876); + message.add_repeated_double(1.234567898765); + message.add_repeated_double(1.2345678987654); + message.add_repeated_double(1.23456789876543); + message.add_repeated_double(1.2e100); + message.add_repeated_double(1.23e100); + message.add_repeated_double(1.234e100); + message.add_repeated_double(1.2345e100); + message.add_repeated_double(1.23456e100); + message.add_repeated_double(1.234567e100); + message.add_repeated_double(1.2345678e100); + message.add_repeated_double(1.23456789e100); + message.add_repeated_double(1.234567898e100); + message.add_repeated_double(1.2345678987e100); + message.add_repeated_double(1.23456789876e100); + message.add_repeated_double(1.234567898765e100); + message.add_repeated_double(1.2345678987654e100); + message.add_repeated_double(1.23456789876543e100); + + EXPECT_EQ( + "repeated_float: 1.2\n" + "repeated_float: 1.23\n" + "repeated_float: 1.234\n" + "repeated_float: 1.2345\n" + "repeated_float: 1.23456\n" + "repeated_float: 1.2e+10\n" + "repeated_float: 1.23e+10\n" + "repeated_float: 1.234e+10\n" + "repeated_float: 1.2345e+10\n" + "repeated_float: 1.23456e+10\n" + "repeated_double: 1.2\n" + "repeated_double: 1.23\n" + "repeated_double: 1.234\n" + "repeated_double: 1.2345\n" + "repeated_double: 1.23456\n" + "repeated_double: 1.234567\n" + "repeated_double: 1.2345678\n" + "repeated_double: 1.23456789\n" + "repeated_double: 1.234567898\n" + "repeated_double: 1.2345678987\n" + "repeated_double: 1.23456789876\n" + "repeated_double: 1.234567898765\n" + "repeated_double: 1.2345678987654\n" + "repeated_double: 1.23456789876543\n" + "repeated_double: 1.2e+100\n" + "repeated_double: 1.23e+100\n" + "repeated_double: 1.234e+100\n" + "repeated_double: 1.2345e+100\n" + "repeated_double: 1.23456e+100\n" + "repeated_double: 1.234567e+100\n" + "repeated_double: 1.2345678e+100\n" + "repeated_double: 1.23456789e+100\n" + "repeated_double: 1.234567898e+100\n" + "repeated_double: 1.2345678987e+100\n" + "repeated_double: 1.23456789876e+100\n" + "repeated_double: 1.234567898765e+100\n" + "repeated_double: 1.2345678987654e+100\n" + "repeated_double: 1.23456789876543e+100\n", + RemoveRedundantZeros(message.DebugString())); +} + + +TEST_F(TextFormatTest, AllowPartial) { + unittest::TestRequired message; + TextFormat::Parser parser; + parser.AllowPartialMessage(true); + EXPECT_TRUE(parser.ParseFromString("a: 1", &message)); + EXPECT_EQ(1, message.a()); + EXPECT_FALSE(message.has_b()); + EXPECT_FALSE(message.has_c()); +} + +TEST_F(TextFormatTest, ParseExotic) { + unittest::TestAllTypes message; + ASSERT_TRUE(TextFormat::ParseFromString( + "repeated_int32: -1\n" + "repeated_int32: -2147483648\n" + "repeated_int64: -1\n" + "repeated_int64: -9223372036854775808\n" + "repeated_uint32: 4294967295\n" + "repeated_uint32: 2147483648\n" + "repeated_uint64: 18446744073709551615\n" + "repeated_uint64: 9223372036854775808\n" + "repeated_double: 123.0\n" + "repeated_double: 123.5\n" + "repeated_double: 0.125\n" + "repeated_double: 1.23E17\n" + "repeated_double: 1.235E+22\n" + "repeated_double: 1.235e-18\n" + "repeated_double: 123.456789\n" + "repeated_double: inf\n" + "repeated_double: Infinity\n" + "repeated_double: -inf\n" + "repeated_double: -Infinity\n" + "repeated_double: nan\n" + "repeated_double: NaN\n" + "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\"\n", + &message)); + + ASSERT_EQ(2, message.repeated_int32_size()); + EXPECT_EQ(-1, message.repeated_int32(0)); + // Note: In C, a negative integer literal is actually the unary negation + // operator being applied to a positive integer literal, and 2147483648 is + // outside the range of int32. However, it is not outside the range of + // uint32. Confusingly, this means that everything works if we make the + // literal unsigned, even though we are negating it. + EXPECT_EQ(-2147483648u, message.repeated_int32(1)); + + ASSERT_EQ(2, message.repeated_int64_size()); + EXPECT_EQ(-1, message.repeated_int64(0)); + // Note: In C, a negative integer literal is actually the unary negation + // operator being applied to a positive integer literal, and + // 9223372036854775808 is outside the range of int64. However, it is not + // outside the range of uint64. Confusingly, this means that everything + // works if we make the literal unsigned, even though we are negating it. + EXPECT_EQ(-GOOGLE_ULONGLONG(9223372036854775808), message.repeated_int64(1)); + + ASSERT_EQ(2, message.repeated_uint32_size()); + EXPECT_EQ(4294967295u, message.repeated_uint32(0)); + EXPECT_EQ(2147483648u, message.repeated_uint32(1)); + + ASSERT_EQ(2, message.repeated_uint64_size()); + EXPECT_EQ(GOOGLE_ULONGLONG(18446744073709551615), message.repeated_uint64(0)); + EXPECT_EQ(GOOGLE_ULONGLONG(9223372036854775808), message.repeated_uint64(1)); + + ASSERT_EQ(13, message.repeated_double_size()); + EXPECT_EQ(123.0 , message.repeated_double(0)); + EXPECT_EQ(123.5 , message.repeated_double(1)); + EXPECT_EQ(0.125 , message.repeated_double(2)); + EXPECT_EQ(1.23E17 , message.repeated_double(3)); + EXPECT_EQ(1.235E22 , message.repeated_double(4)); + EXPECT_EQ(1.235E-18 , message.repeated_double(5)); + EXPECT_EQ(123.456789, message.repeated_double(6)); + EXPECT_EQ(message.repeated_double(7), numeric_limits::infinity()); + EXPECT_EQ(message.repeated_double(8), numeric_limits::infinity()); + EXPECT_EQ(message.repeated_double(9), -numeric_limits::infinity()); + EXPECT_EQ(message.repeated_double(10), -numeric_limits::infinity()); + EXPECT_TRUE(IsNaN(message.repeated_double(11))); + EXPECT_TRUE(IsNaN(message.repeated_double(12))); + + // Note: Since these string literals have \0's in them, we must explicitly + // pass their sizes to string's constructor. + ASSERT_EQ(1, message.repeated_string_size()); + EXPECT_EQ(string("\000\001\a\b\f\n\r\t\v\\\'\"", 12), + message.repeated_string(0)); +} + +class TextFormatParserTest : public testing::Test { + protected: + void ExpectFailure(const string& input, const string& message, int line, + int col) { + unittest::TestAllTypes proto; + ExpectFailure(input, message, line, col, &proto); + } + + void ExpectFailure(const string& input, const string& message, int line, + int col, Message* proto) { + TextFormat::Parser parser; + MockErrorCollector error_collector; + parser.RecordErrorsTo(&error_collector); + EXPECT_FALSE(parser.ParseFromString(input, proto)); + EXPECT_EQ(SimpleItoa(line) + ":" + SimpleItoa(col) + ": " + message + "\n", + error_collector.text_); + } + + // An error collector which simply concatenates all its errors into a big + // block of text which can be checked. + class MockErrorCollector : public io::ErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector ------------------------------------- + void AddError(int line, int column, const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1: $2\n", + line + 1, column + 1, message); + } + }; +}; + +TEST_F(TextFormatParserTest, InvalidToken) { + ExpectFailure("optional_bool: true\n-5\n", "Expected identifier.", + 2, 1); + + ExpectFailure("optional_bool: true;\n", "Expected identifier.", 1, 20); + ExpectFailure("\"some string\"", "Expected identifier.", 1, 1); +} + +TEST_F(TextFormatParserTest, InvalidFieldName) { + ExpectFailure( + "invalid_field: somevalue\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"invalid_field\".", + 1, 14); +} + +TEST_F(TextFormatParserTest, InvalidCapitalization) { + // We require that group names be exactly as they appear in the .proto. + ExpectFailure( + "optionalgroup {\na: 15\n}\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"optionalgroup\".", + 1, 15); + ExpectFailure( + "OPTIONALgroup {\na: 15\n}\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"OPTIONALgroup\".", + 1, 15); + ExpectFailure( + "Optional_Double: 10.0\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"Optional_Double\".", + 1, 16); +} + +TEST_F(TextFormatParserTest, InvalidFieldValues) { + // Invalid values for a double/float field. + ExpectFailure("optional_double: \"hello\"\n", "Expected double.", 1, 18); + ExpectFailure("optional_double: true\n", "Expected double.", 1, 18); + ExpectFailure("optional_double: !\n", "Expected double.", 1, 18); + ExpectFailure("optional_double {\n \n}\n", "Expected \":\", found \"{\".", + 1, 17); + + // Invalid values for a signed integer field. + ExpectFailure("optional_int32: \"hello\"\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32: true\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32: 4.5\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32: !\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32 {\n \n}\n", "Expected \":\", found \"{\".", + 1, 16); + ExpectFailure("optional_int32: 0x80000000\n", + "Integer out of range.", 1, 17); + ExpectFailure("optional_int32: -0x80000001\n", + "Integer out of range.", 1, 18); + ExpectFailure("optional_int64: 0x8000000000000000\n", + "Integer out of range.", 1, 17); + ExpectFailure("optional_int64: -0x8000000000000001\n", + "Integer out of range.", 1, 18); + + // Invalid values for an unsigned integer field. + ExpectFailure("optional_uint64: \"hello\"\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: true\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: 4.5\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: -5\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: !\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64 {\n \n}\n", "Expected \":\", found \"{\".", + 1, 17); + ExpectFailure("optional_uint32: 0x100000000\n", + "Integer out of range.", 1, 18); + ExpectFailure("optional_uint64: 0x10000000000000000\n", + "Integer out of range.", 1, 18); + + // Invalid values for a boolean field. + ExpectFailure("optional_bool: \"hello\"\n", "Expected identifier.", 1, 16); + ExpectFailure("optional_bool: 5\n", "Expected identifier.", 1, 16); + ExpectFailure("optional_bool: -7.5\n", "Expected identifier.", 1, 16); + ExpectFailure("optional_bool: !\n", "Expected identifier.", 1, 16); + + ExpectFailure( + "optional_bool: meh\n", + "Invalid value for boolean field \"optional_bool\". Value: \"meh\".", + 2, 1); + + ExpectFailure("optional_bool {\n \n}\n", "Expected \":\", found \"{\".", + 1, 15); + + // Invalid values for a string field. + ExpectFailure("optional_string: true\n", "Expected string.", 1, 18); + ExpectFailure("optional_string: 5\n", "Expected string.", 1, 18); + ExpectFailure("optional_string: -7.5\n", "Expected string.", 1, 18); + ExpectFailure("optional_string: !\n", "Expected string.", 1, 18); + ExpectFailure("optional_string {\n \n}\n", "Expected \":\", found \"{\".", + 1, 17); + + // Invalid values for an enumeration field. + ExpectFailure("optional_nested_enum: \"hello\"\n", "Expected identifier.", + 1, 23); + + ExpectFailure("optional_nested_enum: 5\n", "Expected identifier.", 1, 23); + ExpectFailure("optional_nested_enum: -7.5\n", "Expected identifier.", 1, 23); + ExpectFailure("optional_nested_enum: !\n", "Expected identifier.", 1, 23); + + ExpectFailure( + "optional_nested_enum: grah\n", + "Unknown enumeration value of \"grah\" for field " + "\"optional_nested_enum\".", 2, 1); + + ExpectFailure( + "optional_nested_enum {\n \n}\n", + "Expected \":\", found \"{\".", 1, 22); +} + +TEST_F(TextFormatParserTest, MessageDelimeters) { + // Non-matching delimeters. + ExpectFailure("OptionalGroup <\n \n}\n", "Expected \">\", found \"}\".", + 3, 1); + + // Invalid delimeters. + ExpectFailure("OptionalGroup [\n \n]\n", "Expected \"{\", found \"[\".", + 1, 15); + + // Unending message. + ExpectFailure("optional_nested_message {\n \nbb: 118\n", + "Expected identifier.", + 4, 1); +} + +TEST_F(TextFormatParserTest, UnknownExtension) { + // Non-matching delimeters. + ExpectFailure("[blahblah]: 123", + "Extension \"blahblah\" is not defined or is not an " + "extension of \"protobuf_unittest.TestAllTypes\".", + 1, 11); +} + +TEST_F(TextFormatParserTest, MissingRequired) { + unittest::TestRequired message; + ExpectFailure("a: 1", + "Message missing required fields: b, c", + 0, 1, &message); +} + +TEST_F(TextFormatParserTest, PrintErrorsToStderr) { + vector errors; + + { + ScopedMemoryLog log; + unittest::TestAllTypes proto; + EXPECT_FALSE(TextFormat::ParseFromString("no_such_field: 1", &proto)); + errors = log.GetMessages(ERROR); + } + + ASSERT_EQ(1, errors.size()); + EXPECT_EQ("Error parsing text-format protobuf_unittest.TestAllTypes: " + "1:14: Message type \"protobuf_unittest.TestAllTypes\" has no field " + "named \"no_such_field\".", + errors[0]); +} + + +class TextFormatMessageSetTest : public testing::Test { + protected: + static const char proto_debug_string_[]; +}; +const char TextFormatMessageSetTest::proto_debug_string_[] = +"message_set {\n" +" [protobuf_unittest.TestMessageSetExtension1] {\n" +" i: 23\n" +" }\n" +" [protobuf_unittest.TestMessageSetExtension2] {\n" +" str: \"foo\"\n" +" }\n" +"}\n"; + + +TEST_F(TextFormatMessageSetTest, Serialize) { + protobuf_unittest::TestMessageSetContainer proto; + protobuf_unittest::TestMessageSetExtension1* item_a = + proto.mutable_message_set()->MutableExtension( + protobuf_unittest::TestMessageSetExtension1::message_set_extension); + item_a->set_i(23); + protobuf_unittest::TestMessageSetExtension2* item_b = + proto.mutable_message_set()->MutableExtension( + protobuf_unittest::TestMessageSetExtension2::message_set_extension); + item_b->set_str("foo"); + EXPECT_EQ(proto_debug_string_, proto.DebugString()); +} + +TEST_F(TextFormatMessageSetTest, Deserialize) { + protobuf_unittest::TestMessageSetContainer proto; + ASSERT_TRUE(TextFormat::ParseFromString(proto_debug_string_, &proto)); + EXPECT_EQ(23, proto.message_set().GetExtension( + protobuf_unittest::TestMessageSetExtension1::message_set_extension).i()); + EXPECT_EQ("foo", proto.message_set().GetExtension( + protobuf_unittest::TestMessageSetExtension2::message_set_extension).str()); + + // Ensure that these are the only entries present. + vector descriptors; + proto.message_set().GetReflection()->ListFields(&descriptors); + EXPECT_EQ(2, descriptors.size()); +} + +} // namespace +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto new file mode 100644 index 00000000..f65c4318 --- /dev/null +++ b/src/google/protobuf/unittest.proto @@ -0,0 +1,452 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file we will use for unit testing. + + +import "google/protobuf/unittest_import.proto"; + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +// In test_util.h we do "using namespace unittest = protobuf_unittest". +package protobuf_unittest; + +// Protos optimized for SPEED use a strict superset of the generated code +// of equivalent ones optimized for CODE_SIZE, so we should optimize all our +// tests for speed unless explicitly testing code size optimization. +option optimize_for = SPEED; + +option java_outer_classname = "UnittestProto"; + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + // The field name "b" fails to compile in proto1 because it conflicts with + // a local variable named "b" in one of the generated methods. Doh. + // This file needs to compile in proto1 to test backwards-compatibility. + optional int32 bb = 1; + } + + enum NestedEnum { + FOO = 1; + BAR = 2; + BAZ = 3; + } + + // Singular + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + + optional group OptionalGroup = 16 { + optional int32 a = 17; + } + + optional NestedMessage optional_nested_message = 18; + optional ForeignMessage optional_foreign_message = 19; + optional protobuf_unittest_import.ImportMessage optional_import_message = 20; + + optional NestedEnum optional_nested_enum = 21; + optional ForeignEnum optional_foreign_enum = 22; + optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; + + optional string optional_string_piece = 24 [ctype=STRING_PIECE]; + optional string optional_cord = 25 [ctype=CORD]; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated group RepeatedGroup = 46 { + optional int32 a = 47; + } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessage repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + repeated protobuf_unittest_import.ImportEnum repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + // Singular with defaults + optional int32 default_int32 = 61 [default = 41 ]; + optional int64 default_int64 = 62 [default = 42 ]; + optional uint32 default_uint32 = 63 [default = 43 ]; + optional uint64 default_uint64 = 64 [default = 44 ]; + optional sint32 default_sint32 = 65 [default = -45 ]; + optional sint64 default_sint64 = 66 [default = 46 ]; + optional fixed32 default_fixed32 = 67 [default = 47 ]; + optional fixed64 default_fixed64 = 68 [default = 48 ]; + optional sfixed32 default_sfixed32 = 69 [default = 49 ]; + optional sfixed64 default_sfixed64 = 70 [default = -50 ]; + optional float default_float = 71 [default = 51.5 ]; + optional double default_double = 72 [default = 52e3 ]; + optional bool default_bool = 73 [default = true ]; + optional string default_string = 74 [default = "hello"]; + optional bytes default_bytes = 75 [default = "world"]; + + optional NestedEnum default_nested_enum = 81 [default = BAR ]; + optional ForeignEnum default_foreign_enum = 82 [default = FOREIGN_BAR]; + optional protobuf_unittest_import.ImportEnum + default_import_enum = 83 [default = IMPORT_BAR]; + + optional string default_string_piece = 84 [ctype=STRING_PIECE,default="abc"]; + optional string default_cord = 85 [ctype=CORD,default="123"]; +} + +// Define these after TestAllTypes to make sure the compiler can handle +// that. +message ForeignMessage { + optional int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +message TestAllExtensions { + extensions 1 to max; +} + +extend TestAllExtensions { + // Singular + optional int32 optional_int32_extension = 1; + optional int64 optional_int64_extension = 2; + optional uint32 optional_uint32_extension = 3; + optional uint64 optional_uint64_extension = 4; + optional sint32 optional_sint32_extension = 5; + optional sint64 optional_sint64_extension = 6; + optional fixed32 optional_fixed32_extension = 7; + optional fixed64 optional_fixed64_extension = 8; + optional sfixed32 optional_sfixed32_extension = 9; + optional sfixed64 optional_sfixed64_extension = 10; + optional float optional_float_extension = 11; + optional double optional_double_extension = 12; + optional bool optional_bool_extension = 13; + optional string optional_string_extension = 14; + optional bytes optional_bytes_extension = 15; + + optional group OptionalGroup_extension = 16 { + optional int32 a = 17; + } + + optional TestAllTypes.NestedMessage optional_nested_message_extension = 18; + optional ForeignMessage optional_foreign_message_extension = 19; + optional protobuf_unittest_import.ImportMessage + optional_import_message_extension = 20; + + optional TestAllTypes.NestedEnum optional_nested_enum_extension = 21; + optional ForeignEnum optional_foreign_enum_extension = 22; + optional protobuf_unittest_import.ImportEnum + optional_import_enum_extension = 23; + + optional string optional_string_piece_extension = 24 [ctype=STRING_PIECE]; + optional string optional_cord_extension = 25 [ctype=CORD]; + + // Repeated + repeated int32 repeated_int32_extension = 31; + repeated int64 repeated_int64_extension = 32; + repeated uint32 repeated_uint32_extension = 33; + repeated uint64 repeated_uint64_extension = 34; + repeated sint32 repeated_sint32_extension = 35; + repeated sint64 repeated_sint64_extension = 36; + repeated fixed32 repeated_fixed32_extension = 37; + repeated fixed64 repeated_fixed64_extension = 38; + repeated sfixed32 repeated_sfixed32_extension = 39; + repeated sfixed64 repeated_sfixed64_extension = 40; + repeated float repeated_float_extension = 41; + repeated double repeated_double_extension = 42; + repeated bool repeated_bool_extension = 43; + repeated string repeated_string_extension = 44; + repeated bytes repeated_bytes_extension = 45; + + repeated group RepeatedGroup_extension = 46 { + optional int32 a = 47; + } + + repeated TestAllTypes.NestedMessage repeated_nested_message_extension = 48; + repeated ForeignMessage repeated_foreign_message_extension = 49; + repeated protobuf_unittest_import.ImportMessage + repeated_import_message_extension = 50; + + repeated TestAllTypes.NestedEnum repeated_nested_enum_extension = 51; + repeated ForeignEnum repeated_foreign_enum_extension = 52; + repeated protobuf_unittest_import.ImportEnum + repeated_import_enum_extension = 53; + + repeated string repeated_string_piece_extension = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord_extension = 55 [ctype=CORD]; + + // Singular with defaults + optional int32 default_int32_extension = 61 [default = 41 ]; + optional int64 default_int64_extension = 62 [default = 42 ]; + optional uint32 default_uint32_extension = 63 [default = 43 ]; + optional uint64 default_uint64_extension = 64 [default = 44 ]; + optional sint32 default_sint32_extension = 65 [default = -45 ]; + optional sint64 default_sint64_extension = 66 [default = 46 ]; + optional fixed32 default_fixed32_extension = 67 [default = 47 ]; + optional fixed64 default_fixed64_extension = 68 [default = 48 ]; + optional sfixed32 default_sfixed32_extension = 69 [default = 49 ]; + optional sfixed64 default_sfixed64_extension = 70 [default = -50 ]; + optional float default_float_extension = 71 [default = 51.5 ]; + optional double default_double_extension = 72 [default = 52e3 ]; + optional bool default_bool_extension = 73 [default = true ]; + optional string default_string_extension = 74 [default = "hello"]; + optional bytes default_bytes_extension = 75 [default = "world"]; + + optional TestAllTypes.NestedEnum + default_nested_enum_extension = 81 [default = BAR]; + optional ForeignEnum + default_foreign_enum_extension = 82 [default = FOREIGN_BAR]; + optional protobuf_unittest_import.ImportEnum + default_import_enum_extension = 83 [default = IMPORT_BAR]; + + optional string default_string_piece_extension = 84 [ctype=STRING_PIECE, + default="abc"]; + optional string default_cord_extension = 85 [ctype=CORD, default="123"]; +} + +// We have separate messages for testing required fields because it's +// annoying to have to fill in required fields in TestProto in order to +// do anything with it. Note that we don't need to test every type of +// required filed because the code output is basically identical to +// optional fields for all types. +message TestRequired { + required int32 a = 1; + optional int32 dummy2 = 2; + required int32 b = 3; + + extend TestAllExtensions { + optional TestRequired single = 1000; + repeated TestRequired multi = 1001; + } + + // Pad the field count to 32 so that we can test that IsInitialized() + // properly checks multiple elements of has_bits_. + optional int32 dummy4 = 4; + optional int32 dummy5 = 5; + optional int32 dummy6 = 6; + optional int32 dummy7 = 7; + optional int32 dummy8 = 8; + optional int32 dummy9 = 9; + optional int32 dummy10 = 10; + optional int32 dummy11 = 11; + optional int32 dummy12 = 12; + optional int32 dummy13 = 13; + optional int32 dummy14 = 14; + optional int32 dummy15 = 15; + optional int32 dummy16 = 16; + optional int32 dummy17 = 17; + optional int32 dummy18 = 18; + optional int32 dummy19 = 19; + optional int32 dummy20 = 20; + optional int32 dummy21 = 21; + optional int32 dummy22 = 22; + optional int32 dummy23 = 23; + optional int32 dummy24 = 24; + optional int32 dummy25 = 25; + optional int32 dummy26 = 26; + optional int32 dummy27 = 27; + optional int32 dummy28 = 28; + optional int32 dummy29 = 29; + optional int32 dummy30 = 30; + optional int32 dummy31 = 31; + optional int32 dummy32 = 32; + + required int32 c = 33; +} + +message TestRequiredForeign { + optional TestRequired optional_message = 1; + repeated TestRequired repeated_message = 2; + optional int32 dummy = 3; +} + +// Test that we can use NestedMessage from outside TestAllTypes. +message TestForeignNested { + optional TestAllTypes.NestedMessage foreign_nested = 1; +} + +// TestEmptyMessage is used to test unknown field support. +message TestEmptyMessage { +} + +// Like above, but declare all field numbers as potential extensions. No +// actual extensions should ever be defined for this type. +message TestEmptyMessageWithExtensions { + extensions 1 to max; +} + +// Test that really large tag numbers don't break anything. +message TestReallyLargeTagNumber { + // The largest possible tag number is 2^28 - 1, since the wire format uses + // three bits to communicate wire type. + optional int32 a = 1; + optional int32 bb = 268435455; +} + +message TestRecursiveMessage { + optional TestRecursiveMessage a = 1; + optional int32 i = 2; +} + +// Test that mutual recursion works. +message TestMutualRecursionA { + optional TestMutualRecursionB bb = 1; +} + +message TestMutualRecursionB { + optional TestMutualRecursionA a = 1; + optional int32 optional_int32 = 2; +} + +// Test that groups have disjoint field numbers from their siblings and +// parents. This is NOT possible in proto1; only proto2. When outputting +// proto1, the dup fields should be dropped. +message TestDupFieldNumber { + optional int32 a = 1; + optional group Foo = 2 { optional int32 a = 1; } + optional group Bar = 3 { optional int32 a = 1; } +} + + +// Needed for a Python test. +message TestNestedMessageHasBits { + message NestedMessage { + repeated int32 nestedmessage_repeated_int32 = 1; + repeated ForeignMessage nestedmessage_repeated_foreignmessage = 2; + } + optional NestedMessage optional_nested_message = 1; +} + + +// Test an enum that has multiple values with the same number. +enum TestEnumWithDupValue { + FOO1 = 1; + BAR1 = 2; + BAZ = 3; + FOO2 = 1; + BAR2 = 2; +} + +// Test an enum with large, unordered values. +enum TestSparseEnum { + SPARSE_A = 123; + SPARSE_B = 62374; + SPARSE_C = 12589234; + SPARSE_D = -15; + SPARSE_E = -53452; + SPARSE_F = 0; + SPARSE_G = 2; +} + +// Test message with CamelCase field names. This violates Protocol Buffer +// standard style. +message TestCamelCaseFieldNames { + optional int32 PrimitiveField = 1; + optional string StringField = 2; + optional ForeignEnum EnumField = 3; + optional ForeignMessage MessageField = 4; + optional string StringPieceField = 5 [ctype=STRING_PIECE]; + optional string CordField = 6 [ctype=CORD]; + + repeated int32 RepeatedPrimitiveField = 7; + repeated string RepeatedStringField = 8; + repeated ForeignEnum RepeatedEnumField = 9; + repeated ForeignMessage RepeatedMessageField = 10; + repeated string RepeatedStringPieceField = 11 [ctype=STRING_PIECE]; + repeated string RepeatedCordField = 12 [ctype=CORD]; +} + + +// We list fields out of order, to ensure that we're using field number and not +// field index to determine serialization order. +message TestFieldOrderings { + optional string my_string = 11; + extensions 2 to 10; + optional int64 my_int = 1; + extensions 12 to 100; + optional float my_float = 101; +} + + +extend TestFieldOrderings { + optional string my_extension_string = 50; + optional int32 my_extension_int = 5; +} + + +message TestExtremeDefaultValues { + optional bytes escaped_bytes = 1 [default = "\0\001\a\b\f\n\r\t\v\\\'\"\xfe"]; + optional uint32 large_uint32 = 2 [default = 0xFFFFFFFF]; + optional uint64 large_uint64 = 3 [default = 0xFFFFFFFFFFFFFFFF]; + optional int32 small_int32 = 4 [default = -0x7FFFFFFF]; + optional int64 small_int64 = 5 [default = -0x7FFFFFFFFFFFFFFF]; + + // The default value here is UTF-8 for "\u1234". (We could also just type + // the UTF-8 text directly into this text file rather than escape it, but + // lots of people use editors that would be confused by this.) + optional string utf8_string = 6 [default = "\341\210\264"]; +} + +// Test that RPC services work. +message FooRequest {} +message FooResponse {} + +service TestService { + rpc Foo(FooRequest) returns (FooResponse); + rpc Bar(BarRequest) returns (BarResponse); +} + + +message BarRequest {} +message BarResponse {} diff --git a/src/google/protobuf/unittest_embed_optimize_for.proto b/src/google/protobuf/unittest_embed_optimize_for.proto new file mode 100644 index 00000000..c600d9fc --- /dev/null +++ b/src/google/protobuf/unittest_embed_optimize_for.proto @@ -0,0 +1,36 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which imports a proto file that uses optimize_for = CODE_SIZE. + +import "google/protobuf/unittest_optimize_for.proto"; + +package protobuf_unittest; + +// We optimize for speed here, but we are importing a proto that is optimized +// for code size. +option optimize_for = SPEED; + +message TestEmbedOptimizedForSize { + // Test that embedding a message which has optimize_for = CODE_SIZE into + // one optimized for speed works. + optional TestOptimizedForSize optional_message = 1; + repeated TestOptimizedForSize repeated_message = 2; +} diff --git a/src/google/protobuf/unittest_import.proto b/src/google/protobuf/unittest_import.proto new file mode 100644 index 00000000..58ce42c3 --- /dev/null +++ b/src/google/protobuf/unittest_import.proto @@ -0,0 +1,47 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which is imported by unittest.proto to test importing. + + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +// In test_util.h we do +// "using namespace unittest_import = protobuf_unittest_import". +package protobuf_unittest_import; + +option optimize_for = SPEED; + +// Excercise the java_package option. +option java_package = "com.google.protobuf.test"; + +// Do not set a java_outer_classname here to verify that Proto2 works without +// one. + +message ImportMessage { + optional int32 d = 1; +} + +enum ImportEnum { + IMPORT_FOO = 7; + IMPORT_BAR = 8; + IMPORT_BAZ = 9; +} + diff --git a/src/google/protobuf/unittest_mset.proto b/src/google/protobuf/unittest_mset.proto new file mode 100644 index 00000000..455086d2 --- /dev/null +++ b/src/google/protobuf/unittest_mset.proto @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// 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 message_set_wire_format. + +package protobuf_unittest; + +option optimize_for = SPEED; + +// A message with message_set_wire_format. +message TestMessageSet { + option message_set_wire_format = true; + extensions 4 to max; +} + +message TestMessageSetContainer { + optional TestMessageSet message_set = 1; +} + +message TestMessageSetExtension1 { + extend TestMessageSet { + optional TestMessageSetExtension1 message_set_extension = 1545008; + } + optional int32 i = 15; +} + +message TestMessageSetExtension2 { + extend TestMessageSet { + optional TestMessageSetExtension2 message_set_extension = 1547769; + } + optional string str = 25; +} + +// MessageSet wire format is equivalent to this. +message RawMessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required bytes message = 3; + } +} + diff --git a/src/google/protobuf/unittest_optimize_for.proto b/src/google/protobuf/unittest_optimize_for.proto new file mode 100644 index 00000000..6154e9c5 --- /dev/null +++ b/src/google/protobuf/unittest_optimize_for.proto @@ -0,0 +1,38 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which uses optimize_for = CODE_SIZE. + +import "google/protobuf/unittest.proto"; + +package protobuf_unittest; + +option optimize_for = CODE_SIZE; + +message TestOptimizedForSize { + optional int32 i = 1; + optional ForeignMessage msg = 19; + + extensions 1000 to max; + + extend TestOptimizedForSize { + optional int32 test_extension = 1234; + } +} diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc new file mode 100644 index 00000000..2f44901e --- /dev/null +++ b/src/google/protobuf/unknown_field_set.cc @@ -0,0 +1,112 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +namespace google { +namespace protobuf { + +UnknownFieldSet::UnknownFieldSet() + : internal_(NULL) {} + +UnknownFieldSet::~UnknownFieldSet() { + if (internal_ != NULL) { + STLDeleteValues(&internal_->fields_); + delete internal_; + } +} + +void UnknownFieldSet::Clear() { + if (internal_ == NULL) return; + + if (internal_->fields_.size() > kMaxInactiveFields) { + STLDeleteValues(&internal_->fields_); + } else { + // Don't delete the UnknownField objects. Just remove them from the active + // set. + for (int i = 0; i < internal_->active_fields_.size(); i++) { + internal_->active_fields_[i]->Clear(); + internal_->active_fields_[i]->index_ = -1; + } + } + + internal_->active_fields_.clear(); +} + +void UnknownFieldSet::MergeFrom(const UnknownFieldSet& other) { + for (int i = 0; i < other.field_count(); i++) { + AddField(other.field(i).number())->MergeFrom(other.field(i)); + } +} + +const UnknownField* UnknownFieldSet::FindFieldByNumber(int number) const { + if (internal_ == NULL) return NULL; + + map::iterator iter = internal_->fields_.find(number); + if (iter != internal_->fields_.end() && iter->second->index() != -1) { + return iter->second; + } else { + return NULL; + } +} + +UnknownField* UnknownFieldSet::AddField(int number) { + if (internal_ == NULL) internal_ = new Internal; + + UnknownField** map_slot = &internal_->fields_[number]; + if (*map_slot == NULL) { + *map_slot = new UnknownField(number); + } + + UnknownField* field = *map_slot; + if (field->index() == -1) { + field->index_ = internal_->active_fields_.size(); + internal_->active_fields_.push_back(field); + } + return field; +} + +UnknownField::UnknownField(int number) + : number_(number), + index_(-1) { +} + +UnknownField::~UnknownField() { +} + +void UnknownField::Clear() { + clear_varint(); + clear_fixed32(); + clear_fixed64(); + clear_length_delimited(); + clear_group(); +} + +void UnknownField::MergeFrom(const UnknownField& other) { + varint_ .MergeFrom(other.varint_ ); + fixed32_ .MergeFrom(other.fixed32_ ); + fixed64_ .MergeFrom(other.fixed64_ ); + length_delimited_.MergeFrom(other.length_delimited_); + group_ .MergeFrom(other.group_ ); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/unknown_field_set.h b/src/google/protobuf/unknown_field_set.h new file mode 100644 index 00000000..42184621 --- /dev/null +++ b/src/google/protobuf/unknown_field_set.h @@ -0,0 +1,322 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Contains classes used to keep track of unrecognized fields seen while +// parsing a protocol message. + +#ifndef GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__ +#define GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +class Message; // message.h +class UnknownField; // below + +// An UnknownFieldSet contains fields that were encountered while parsing a +// message but were not defined by its type. Keeping track of these can be +// useful, especially in that they may be written if the message is serialized +// again without being cleared in between. This means that software which +// simply receives messages and forwards them to other servers does not need +// to be updated every time a new field is added to the message definition. +// +// To get the UnknownFieldSet attached to any message, call +// Message::Reflection::GetUnknownFields(). +// +// This class is necessarily tied to the protocol buffer wire format, unlike +// the Reflection interface which is independent of any serialization scheme. +class LIBPROTOBUF_EXPORT UnknownFieldSet { + public: + UnknownFieldSet(); + ~UnknownFieldSet(); + + // Remove all fields. + void Clear(); + + // Is this set empty? + inline bool empty() const; + + // Merge the contents of some other UnknownFieldSet with this one. + void MergeFrom(const UnknownFieldSet& other); + + // Returns the number of fields present in the UnknownFieldSet. + inline int field_count() const; + // Get a field in the set, where 0 <= index < field_count(). The fields + // appear in arbitrary order. + inline const UnknownField& field(int index) const; + // Get a mutable pointer to a field in the set, where + // 0 <= index < field_count(). The fields appear in arbitrary order. + inline UnknownField* mutable_field(int index); + + // Find a field by field number. Returns NULL if not found. + const UnknownField* FindFieldByNumber(int number) const; + + // Add a field by field number. If the field number already exists, returns + // the existing UnknownField. + UnknownField* AddField(int number); + + private: + // "Active" fields are ones which have been added since the last time Clear() + // was called. Inactive fields are objects we are keeping around incase + // they become active again. + + struct Internal { + // Contains all UnknownFields that have been allocated for this + // UnknownFieldSet, including ones not currently active. Keyed by + // field number. We intentionally try to reuse UnknownField objects for + // the same field number they were used for originally because this makes + // it more likely that the previously-allocated memory will have the right + // layout. + map fields_; + + // Contains the fields from fields_ that are currently active. + vector active_fields_; + }; + + // We want an UnknownFieldSet to use no more space than a single pointer + // until the first field is added. + Internal* internal_; + + // Don't keep more inactive fields than this. + static const int kMaxInactiveFields = 100; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UnknownFieldSet); +}; + +// Represents one field in an UnknownFieldSet. +// +// UnknownFiled's accessors are similar to those that would be produced by the +// protocol compiler for the fields: +// repeated uint64 varint; +// repeated fixed32 fixed32; +// repeated fixed64 fixed64; +// repeated bytes length_delimited; +// repeated UnknownFieldSet group; +// (OK, so the last one isn't actually a valid field type but you get the +// idea.) +class LIBPROTOBUF_EXPORT UnknownField { + public: + ~UnknownField(); + + // Clears all fields. + void Clear(); + + // Merge the contents of some other UnknownField with this one. For each + // wire type, the values are simply concatenated. + void MergeFrom(const UnknownField& other); + + // The field's tag number, as seen on the wire. + inline int number() const; + + // The index of this UnknownField within the UknownFieldSet (e.g. + // set.field(field.index()) == field). + inline int index() const; + + inline int varint_size () const; + inline int fixed32_size () const; + inline int fixed64_size () const; + inline int length_delimited_size() const; + inline int group_size () const; + + inline uint64 varint (int index) const; + inline uint32 fixed32(int index) const; + inline uint64 fixed64(int index) const; + inline const string& length_delimited(int index) const; + inline const UnknownFieldSet& group(int index) const; + + inline void set_varint (int index, uint64 value); + inline void set_fixed32(int index, uint32 value); + inline void set_fixed64(int index, uint64 value); + inline void set_length_delimited(int index, const string& value); + inline string* mutable_length_delimited(int index); + inline UnknownFieldSet* mutable_group(int index); + + inline void add_varint (uint64 value); + inline void add_fixed32(uint32 value); + inline void add_fixed64(uint64 value); + inline void add_length_delimited(const string& value); + inline string* add_length_delimited(); + inline UnknownFieldSet* add_group(); + + inline void clear_varint (); + inline void clear_fixed32(); + inline void clear_fixed64(); + inline void clear_length_delimited(); + inline void clear_group(); + + inline const RepeatedField & varint () const; + inline const RepeatedField & fixed32 () const; + inline const RepeatedField & fixed64 () const; + inline const RepeatedPtrField& length_delimited() const; + inline const RepeatedPtrField& group () const; + + inline RepeatedField * mutable_varint (); + inline RepeatedField * mutable_fixed32 (); + inline RepeatedField * mutable_fixed64 (); + inline RepeatedPtrField* mutable_length_delimited(); + inline RepeatedPtrField* mutable_group (); + + private: + friend class UnknownFieldSet; + UnknownField(int number); + + int number_; + int index_; + + RepeatedField varint_; + RepeatedField fixed32_; + RepeatedField fixed64_; + RepeatedPtrField length_delimited_; + RepeatedPtrField group_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UnknownField); +}; + +// =================================================================== +// inline implementations + +inline bool UnknownFieldSet::empty() const { + return internal_ == NULL || internal_->active_fields_.empty(); +} + +inline int UnknownFieldSet::field_count() const { + return (internal_ == NULL) ? 0 : internal_->active_fields_.size(); +} +inline const UnknownField& UnknownFieldSet::field(int index) const { + return *(internal_->active_fields_[index]); +} +inline UnknownField* UnknownFieldSet::mutable_field(int index) { + return internal_->active_fields_[index]; +} + +inline int UnknownField::number() const { return number_; } +inline int UnknownField::index () const { return index_; } + +inline int UnknownField::varint_size () const {return varint_.size();} +inline int UnknownField::fixed32_size () const {return fixed32_.size();} +inline int UnknownField::fixed64_size () const {return fixed64_.size();} +inline int UnknownField::length_delimited_size() const { + return length_delimited_.size(); +} +inline int UnknownField::group_size () const {return group_.size();} + +inline uint64 UnknownField::varint (int index) const { + return varint_.Get(index); +} +inline uint32 UnknownField::fixed32(int index) const { + return fixed32_.Get(index); +} +inline uint64 UnknownField::fixed64(int index) const { + return fixed64_.Get(index); +} +inline const string& UnknownField::length_delimited(int index) const { + return length_delimited_.Get(index); +} +inline const UnknownFieldSet& UnknownField::group(int index) const { + return group_.Get(index); +} + +inline void UnknownField::set_varint (int index, uint64 value) { + varint_.Set(index, value); +} +inline void UnknownField::set_fixed32(int index, uint32 value) { + fixed32_.Set(index, value); +} +inline void UnknownField::set_fixed64(int index, uint64 value) { + fixed64_.Set(index, value); +} +inline void UnknownField::set_length_delimited(int index, const string& value) { + length_delimited_.Mutable(index)->assign(value); +} +inline string* UnknownField::mutable_length_delimited(int index) { + return length_delimited_.Mutable(index); +} +inline UnknownFieldSet* UnknownField::mutable_group(int index) { + return group_.Mutable(index); +} + +inline void UnknownField::add_varint (uint64 value) { + varint_.Add(value); +} +inline void UnknownField::add_fixed32(uint32 value) { + fixed32_.Add(value); +} +inline void UnknownField::add_fixed64(uint64 value) { + fixed64_.Add(value); +} +inline void UnknownField::add_length_delimited(const string& value) { + length_delimited_.Add()->assign(value); +} +inline string* UnknownField::add_length_delimited() { + return length_delimited_.Add(); +} +inline UnknownFieldSet* UnknownField::add_group() { + return group_.Add(); +} + +inline void UnknownField::clear_varint () { varint_.Clear(); } +inline void UnknownField::clear_fixed32() { varint_.Clear(); } +inline void UnknownField::clear_fixed64() { varint_.Clear(); } +inline void UnknownField::clear_length_delimited() { + length_delimited_.Clear(); +} +inline void UnknownField::clear_group() { group_.Clear(); } + +inline const RepeatedField& UnknownField::varint () const { + return varint_; +} +inline const RepeatedField& UnknownField::fixed32() const { + return fixed32_; +} +inline const RepeatedField& UnknownField::fixed64() const { + return fixed64_; +} +inline const RepeatedPtrField& UnknownField::length_delimited() const { + return length_delimited_; +} +inline const RepeatedPtrField& UnknownField::group() const { + return group_; +} + +inline RepeatedField* UnknownField::mutable_varint () { + return &varint_; +} +inline RepeatedField* UnknownField::mutable_fixed32() { + return &fixed32_; +} +inline RepeatedField* UnknownField::mutable_fixed64() { + return &fixed64_; +} +inline RepeatedPtrField* UnknownField::mutable_length_delimited() { + return &length_delimited_; +} +inline RepeatedPtrField* UnknownField::mutable_group() { + return &group_; +} + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__ diff --git a/src/google/protobuf/unknown_field_set_unittest.cc b/src/google/protobuf/unknown_field_set_unittest.cc new file mode 100644 index 00000000..39b005f4 --- /dev/null +++ b/src/google/protobuf/unknown_field_set_unittest.cc @@ -0,0 +1,424 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This test is testing a lot more than just the UnknownFieldSet class. It +// tests handling of unknown fields throughout the system. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +using internal::WireFormat; + +namespace { + +class UnknownFieldSetTest : public testing::Test { + protected: + virtual void SetUp() { + descriptor_ = unittest::TestAllTypes::descriptor(); + TestUtil::SetAllFields(&all_fields_); + all_fields_.SerializeToString(&all_fields_data_); + ASSERT_TRUE(empty_message_.ParseFromString(all_fields_data_)); + unknown_fields_ = empty_message_.mutable_unknown_fields(); + } + + const UnknownField* GetField(const string& name) { + const FieldDescriptor* field = descriptor_->FindFieldByName(name); + if (field == NULL) return NULL; + return unknown_fields_->FindFieldByNumber(field->number()); + } + + // Constructs a protocol buffer which contains fields with all the same + // numbers as all_fields_data_ except that each field is some other wire + // type. + string GetBizarroData() { + unittest::TestEmptyMessage bizarro_message; + UnknownFieldSet* bizarro_unknown_fields = + bizarro_message.mutable_unknown_fields(); + for (int i = 0; i < unknown_fields_->field_count(); i++) { + const UnknownField& unknown_field = unknown_fields_->field(i); + UnknownField* bizarro_field = + bizarro_unknown_fields->AddField(unknown_field.number()); + if (unknown_field.varint_size() == 0) { + bizarro_field->add_varint(1); + } else { + bizarro_field->add_fixed32(1); + } + } + + string data; + EXPECT_TRUE(bizarro_message.SerializeToString(&data)); + return data; + } + + const Descriptor* descriptor_; + unittest::TestAllTypes all_fields_; + string all_fields_data_; + + // An empty message that has been parsed from all_fields_data_. So, it has + // unknown fields of every type. + unittest::TestEmptyMessage empty_message_; + UnknownFieldSet* unknown_fields_; +}; + +TEST_F(UnknownFieldSetTest, Index) { + for (int i = 0; i < unknown_fields_->field_count(); i++) { + EXPECT_EQ(i, unknown_fields_->field(i).index()); + } +} + +TEST_F(UnknownFieldSetTest, FindFieldByNumber) { + // All fields of TestAllTypes should be present. Fields that are not valid + // field numbers of TestAllTypes should NOT be present. + + for (int i = 0; i < 1000; i++) { + if (descriptor_->FindFieldByNumber(i) == NULL) { + EXPECT_TRUE(unknown_fields_->FindFieldByNumber(i) == NULL); + } else { + EXPECT_TRUE(unknown_fields_->FindFieldByNumber(i) != NULL); + } + } +} + +TEST_F(UnknownFieldSetTest, Varint) { + const UnknownField* field = GetField("optional_int32"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->varint_size()); + EXPECT_EQ(all_fields_.optional_int32(), field->varint(0)); +} + +TEST_F(UnknownFieldSetTest, Fixed32) { + const UnknownField* field = GetField("optional_fixed32"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->fixed32_size()); + EXPECT_EQ(all_fields_.optional_fixed32(), field->fixed32(0)); +} + +TEST_F(UnknownFieldSetTest, Fixed64) { + const UnknownField* field = GetField("optional_fixed64"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->fixed64_size()); + EXPECT_EQ(all_fields_.optional_fixed64(), field->fixed64(0)); +} + +TEST_F(UnknownFieldSetTest, LengthDelimited) { + const UnknownField* field = GetField("optional_string"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->length_delimited_size()); + EXPECT_EQ(all_fields_.optional_string(), field->length_delimited(0)); +} + +TEST_F(UnknownFieldSetTest, Group) { + const UnknownField* field = GetField("optionalgroup"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->group_size()); + EXPECT_EQ(1, field->group(0).field_count()); + + const UnknownField& nested_field = field->group(0).field(0); + const FieldDescriptor* nested_field_descriptor = + unittest::TestAllTypes::OptionalGroup::descriptor()->FindFieldByName("a"); + ASSERT_TRUE(nested_field_descriptor != NULL); + + EXPECT_EQ(nested_field_descriptor->number(), nested_field.number()); + EXPECT_EQ(all_fields_.optionalgroup().a(), nested_field.varint(0)); +} + +TEST_F(UnknownFieldSetTest, Serialize) { + // Check that serializing the UnknownFieldSet produces the original data + // again. + + string data; + empty_message_.SerializeToString(&data); + + // Don't use EXPECT_EQ because we don't want to dump raw binary data to + // stdout. + EXPECT_TRUE(data == all_fields_data_); +} + +TEST_F(UnknownFieldSetTest, ParseViaReflection) { + // Make sure fields are properly parsed to the UnknownFieldSet when parsing + // via reflection. + + unittest::TestEmptyMessage message; + io::ArrayInputStream raw_input(all_fields_data_.data(), + all_fields_data_.size()); + io::CodedInputStream input(&raw_input); + ASSERT_TRUE(WireFormat::ParseAndMergePartial(message.GetDescriptor(), &input, + message.GetReflection())); + + EXPECT_EQ(message.DebugString(), empty_message_.DebugString()); +} + +TEST_F(UnknownFieldSetTest, SerializeViaReflection) { + // Make sure fields are properly written from the UnknownFieldSet when + // serializing via reflection. + + string data; + + { + io::StringOutputStream raw_output(&data); + io::CodedOutputStream output(&raw_output); + int size = WireFormat::ByteSize(empty_message_.GetDescriptor(), + empty_message_.GetReflection()); + ASSERT_TRUE( + WireFormat::SerializeWithCachedSizes(empty_message_.GetDescriptor(), + empty_message_.GetReflection(), + size, &output)); + } + + // Don't use EXPECT_EQ because we don't want to dump raw binary data to + // stdout. + EXPECT_TRUE(data == all_fields_data_); +} + +TEST_F(UnknownFieldSetTest, CopyFrom) { + unittest::TestEmptyMessage message; + + message.CopyFrom(empty_message_); + + EXPECT_EQ(empty_message_.DebugString(), message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, MergeFrom) { + unittest::TestEmptyMessage source, destination; + + destination.mutable_unknown_fields()->AddField(1)->add_varint(1); + destination.mutable_unknown_fields()->AddField(3)->add_varint(2); + source.mutable_unknown_fields()->AddField(2)->add_varint(3); + source.mutable_unknown_fields()->AddField(3)->add_varint(4); + + destination.MergeFrom(source); + + EXPECT_EQ( + // Note: The ordering of fields here depends on the ordering of adds + // and merging, above. + "1: 1\n" + "3: 2\n" + "3: 4\n" + "2: 3\n", + destination.DebugString()); +} + +TEST_F(UnknownFieldSetTest, Clear) { + // Get a pointer to a contained field object. + const UnknownField* field = GetField("optional_int32"); + ASSERT_TRUE(field != NULL); + ASSERT_EQ(1, field->varint_size()); + int number = field->number(); + + // Clear the set. + empty_message_.Clear(); + EXPECT_EQ(0, unknown_fields_->field_count()); + + // If we add that field again we should get the same object. + ASSERT_EQ(field, unknown_fields_->AddField(number)); + + // But it should be cleared. + EXPECT_EQ(0, field->varint_size()); +} + +TEST_F(UnknownFieldSetTest, ParseKnownAndUnknown) { + // Test mixing known and unknown fields when parsing. + + unittest::TestEmptyMessage source; + source.mutable_unknown_fields()->AddField(123456)->add_varint(654321); + string data; + ASSERT_TRUE(source.SerializeToString(&data)); + + unittest::TestAllTypes destination; + ASSERT_TRUE(destination.ParseFromString(all_fields_data_ + data)); + + TestUtil::ExpectAllFieldsSet(destination); + ASSERT_EQ(1, destination.unknown_fields().field_count()); + ASSERT_EQ(1, destination.unknown_fields().field(0).varint_size()); + EXPECT_EQ(654321, destination.unknown_fields().field(0).varint(0)); +} + +TEST_F(UnknownFieldSetTest, WrongTypeTreatedAsUnknown) { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing. + + unittest::TestAllTypes all_types_message; + unittest::TestEmptyMessage empty_message; + string bizarro_data = GetBizarroData(); + ASSERT_TRUE(all_types_message.ParseFromString(bizarro_data)); + ASSERT_TRUE(empty_message.ParseFromString(bizarro_data)); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + EXPECT_EQ(empty_message.DebugString(), all_types_message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, WrongTypeTreatedAsUnknownViaReflection) { + // Same as WrongTypeTreatedAsUnknown but via the reflection interface. + + unittest::TestAllTypes all_types_message; + unittest::TestEmptyMessage empty_message; + string bizarro_data = GetBizarroData(); + io::ArrayInputStream raw_input(bizarro_data.data(), bizarro_data.size()); + io::CodedInputStream input(&raw_input); + ASSERT_TRUE(WireFormat::ParseAndMergePartial( + all_types_message.GetDescriptor(), &input, + all_types_message.GetReflection())); + ASSERT_TRUE(empty_message.ParseFromString(bizarro_data)); + + EXPECT_EQ(empty_message.DebugString(), all_types_message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, UnknownExtensions) { + // Make sure fields are properly parsed to the UnknownFieldSet even when + // they are declared as extension numbers. + + unittest::TestEmptyMessageWithExtensions message; + ASSERT_TRUE(message.ParseFromString(all_fields_data_)); + + EXPECT_EQ(message.DebugString(), empty_message_.DebugString()); +} + +TEST_F(UnknownFieldSetTest, UnknownExtensionsReflection) { + // Same as UnknownExtensions except parsing via reflection. + + unittest::TestEmptyMessageWithExtensions message; + io::ArrayInputStream raw_input(all_fields_data_.data(), + all_fields_data_.size()); + io::CodedInputStream input(&raw_input); + ASSERT_TRUE(WireFormat::ParseAndMergePartial(message.GetDescriptor(), &input, + message.GetReflection())); + + EXPECT_EQ(message.DebugString(), empty_message_.DebugString()); +} + +TEST_F(UnknownFieldSetTest, WrongExtensionTypeTreatedAsUnknown) { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing extensions. + + unittest::TestAllExtensions all_extensions_message; + unittest::TestEmptyMessage empty_message; + string bizarro_data = GetBizarroData(); + ASSERT_TRUE(all_extensions_message.ParseFromString(bizarro_data)); + ASSERT_TRUE(empty_message.ParseFromString(bizarro_data)); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + EXPECT_EQ(empty_message.DebugString(), all_extensions_message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, UnknownEnumValue) { + using unittest::TestAllTypes; + using unittest::TestAllExtensions; + using unittest::TestEmptyMessage; + + const FieldDescriptor* singular_field = + TestAllTypes::descriptor()->FindFieldByName("optional_nested_enum"); + const FieldDescriptor* repeated_field = + TestAllTypes::descriptor()->FindFieldByName("repeated_nested_enum"); + ASSERT_TRUE(singular_field != NULL); + ASSERT_TRUE(repeated_field != NULL); + + string data; + + { + TestEmptyMessage empty_message; + UnknownFieldSet* unknown_fields = empty_message.mutable_unknown_fields(); + UnknownField* singular_unknown_field = + unknown_fields->AddField(singular_field->number()); + singular_unknown_field->add_varint(TestAllTypes::BAR); + singular_unknown_field->add_varint(5); // not valid + UnknownField* repeated_unknown_field = + unknown_fields->AddField(repeated_field->number()); + repeated_unknown_field->add_varint(TestAllTypes::FOO); + repeated_unknown_field->add_varint(4); // not valid + repeated_unknown_field->add_varint(TestAllTypes::BAZ); + repeated_unknown_field->add_varint(6); // not valid + empty_message.SerializeToString(&data); + } + + { + TestAllTypes message; + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(TestAllTypes::BAR, message.optional_nested_enum()); + ASSERT_EQ(2, message.repeated_nested_enum_size()); + EXPECT_EQ(TestAllTypes::FOO, message.repeated_nested_enum(0)); + EXPECT_EQ(TestAllTypes::BAZ, message.repeated_nested_enum(1)); + + const UnknownFieldSet& unknown_fields = message.unknown_fields(); + ASSERT_EQ(2, unknown_fields.field_count()); + + const UnknownField& singular_unknown_field = unknown_fields.field(0); + ASSERT_EQ(singular_field->number(), singular_unknown_field.number()); + ASSERT_EQ(1, singular_unknown_field.varint_size()); + EXPECT_EQ(5, singular_unknown_field.varint(0)); + + const UnknownField& repeated_unknown_field = unknown_fields.field(1); + ASSERT_EQ(repeated_field->number(), repeated_unknown_field.number()); + ASSERT_EQ(2, repeated_unknown_field.varint_size()); + EXPECT_EQ(4, repeated_unknown_field.varint(0)); + EXPECT_EQ(6, repeated_unknown_field.varint(1)); + } + + { + using unittest::optional_nested_enum_extension; + using unittest::repeated_nested_enum_extension; + + TestAllExtensions message; + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(TestAllTypes::BAR, + message.GetExtension(optional_nested_enum_extension)); + ASSERT_EQ(2, message.ExtensionSize(repeated_nested_enum_extension)); + EXPECT_EQ(TestAllTypes::FOO, + message.GetExtension(repeated_nested_enum_extension, 0)); + EXPECT_EQ(TestAllTypes::BAZ, + message.GetExtension(repeated_nested_enum_extension, 1)); + + const UnknownFieldSet& unknown_fields = message.unknown_fields(); + ASSERT_EQ(2, unknown_fields.field_count()); + + const UnknownField& singular_unknown_field = unknown_fields.field(0); + ASSERT_EQ(singular_field->number(), singular_unknown_field.number()); + ASSERT_EQ(1, singular_unknown_field.varint_size()); + EXPECT_EQ(5, singular_unknown_field.varint(0)); + + const UnknownField& repeated_unknown_field = unknown_fields.field(1); + ASSERT_EQ(repeated_field->number(), repeated_unknown_field.number()); + ASSERT_EQ(2, repeated_unknown_field.varint_size()); + EXPECT_EQ(4, repeated_unknown_field.varint(0)); + EXPECT_EQ(6, repeated_unknown_field.varint(1)); + } +} + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc new file mode 100644 index 00000000..77e9c74b --- /dev/null +++ b/src/google/protobuf/wire_format.cc @@ -0,0 +1,801 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +namespace { + +// This function turns out to be convenient when using some macros later. +inline int GetEnumNumber(const EnumValueDescriptor* descriptor) { + return descriptor->number(); +} + +// These are the tags for the old MessageSet format, which was defined as: +// message MessageSet { +// repeated group Item = 1 { +// required int32 type_id = 2; +// required string message = 3; +// } +// } +const int kMessageSetItemStartTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(1, WireFormat::WIRETYPE_START_GROUP); +const int kMessageSetItemEndTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(1, WireFormat::WIRETYPE_END_GROUP); +const int kMessageSetTypeIdTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(2, WireFormat::WIRETYPE_VARINT); +const int kMessageSetMessageTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(3, WireFormat::WIRETYPE_LENGTH_DELIMITED); + +// Byte size of all tags of a MessageSet::Item combined. +static const int kMessageSetItemTagsSize = + io::CodedOutputStream::VarintSize32(kMessageSetItemStartTag) + + io::CodedOutputStream::VarintSize32(kMessageSetItemEndTag) + + io::CodedOutputStream::VarintSize32(kMessageSetTypeIdTag) + + io::CodedOutputStream::VarintSize32(kMessageSetMessageTag); + +} // anonymous namespace + +const WireFormat::WireType +WireFormat::kWireTypeForFieldType[FieldDescriptor::MAX_TYPE + 1] = { + static_cast(-1), // invalid + WIRETYPE_FIXED64, // TYPE_DOUBLE + WIRETYPE_FIXED32, // TYPE_FLOAT + WIRETYPE_VARINT, // TYPE_INT64 + WIRETYPE_VARINT, // TYPE_UINT64 + WIRETYPE_VARINT, // TYPE_INT32 + WIRETYPE_FIXED64, // TYPE_FIXED64 + WIRETYPE_FIXED32, // TYPE_FIXED32 + WIRETYPE_VARINT, // TYPE_BOOL + WIRETYPE_LENGTH_DELIMITED, // TYPE_STRING + WIRETYPE_START_GROUP, // TYPE_GROUP + WIRETYPE_LENGTH_DELIMITED, // TYPE_MESSAGE + WIRETYPE_LENGTH_DELIMITED, // TYPE_BYTES + WIRETYPE_VARINT, // TYPE_UINT32 + WIRETYPE_VARINT, // TYPE_ENUM + WIRETYPE_FIXED32, // TYPE_SFIXED32 + WIRETYPE_FIXED64, // TYPE_SFIXED64 + WIRETYPE_VARINT, // TYPE_SINT32 + WIRETYPE_VARINT, // TYPE_SINT64 +}; + +// =================================================================== + +bool WireFormat::SkipField(io::CodedInputStream* input, uint32 tag, + UnknownFieldSet* unknown_fields) { + UnknownField* field = (unknown_fields == NULL) ? NULL : + unknown_fields->AddField(GetTagFieldNumber(tag)); + + switch (GetTagWireType(tag)) { + case WIRETYPE_VARINT: { + uint64 value; + if (!input->ReadVarint64(&value)) return false; + if (field != NULL) field->add_varint(value); + return true; + } + case WIRETYPE_FIXED64: { + uint64 value; + if (!input->ReadLittleEndian64(&value)) return false; + if (field != NULL) field->add_fixed64(value); + return true; + } + case WIRETYPE_LENGTH_DELIMITED: { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (field == NULL) { + if (!input->Skip(length)) return false; + } else { + input->ReadString(field->add_length_delimited(), length); + } + return true; + } + case WIRETYPE_START_GROUP: { + if (!input->IncrementRecursionDepth()) return false; + if (!SkipMessage(input, (field == NULL) ? NULL : field->add_group())) { + return false; + } + input->DecrementRecursionDepth(); + // Check that the ending tag matched the starting tag. + if (!input->LastTagWas( + MakeTag(GetTagFieldNumber(tag), WIRETYPE_END_GROUP))) { + return false; + } + return true; + } + case WIRETYPE_END_GROUP: { + return false; + } + case WIRETYPE_FIXED32: { + uint32 value; + if (!input->ReadLittleEndian32(&value)) return false; + if (field != NULL) field->add_fixed32(value); + return true; + } + default: { + return false; + } + } +} + +bool WireFormat::SkipMessage(io::CodedInputStream* input, + UnknownFieldSet* unknown_fields) { + while(true) { + uint32 tag = input->ReadTag(); + if (tag == 0) { + // End of input. This is a valid place to end, so return true. + return true; + } + + WireType wire_type = GetTagWireType(tag); + + if (wire_type == WIRETYPE_END_GROUP) { + // Must be the end of the message. + return true; + } + + if (!SkipField(input, tag, unknown_fields)) return false; + } +} + +bool WireFormat::SerializeUnknownFields(const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output) { + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + +#define DO(EXPRESSION) if (!(EXPRESSION)) return false + for (int j = 0; j < field.varint_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_VARINT))); + DO(output->WriteVarint64(field.varint(j))); + } + for (int j = 0; j < field.fixed32_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_FIXED32))); + DO(output->WriteLittleEndian32(field.fixed32(j))); + } + for (int j = 0; j < field.fixed64_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_FIXED64))); + DO(output->WriteLittleEndian64(field.fixed64(j))); + } + for (int j = 0; j < field.length_delimited_size(); j++) { + DO(output->WriteVarint32( + MakeTag(field.number(), WIRETYPE_LENGTH_DELIMITED))); + DO(output->WriteVarint32(field.length_delimited(j).size())); + DO(output->WriteString(field.length_delimited(j))); + } + for (int j = 0; j < field.group_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_START_GROUP))); + DO(SerializeUnknownFields(field.group(j), output)); + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_END_GROUP))); + } +#undef DO + } + + return true; +} + +bool WireFormat::SerializeUnknownMessageSetItems( + const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output) { + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + +#define DO(EXPRESSION) if (!(EXPRESSION)) return false + // The only unknown fields that are allowed to exist in a MessageSet are + // messages, which are length-delimited. + for (int j = 0; j < field.length_delimited_size(); j++) { + const string& data = field.length_delimited(j); + + // Start group. + DO(output->WriteVarint32(kMessageSetItemStartTag)); + + // Write type ID. + DO(output->WriteVarint32(kMessageSetTypeIdTag)); + DO(output->WriteVarint32(field.number())); + + // Write message. + DO(output->WriteVarint32(kMessageSetMessageTag)); + DO(output->WriteVarint32(data.size())); + DO(output->WriteString(data)); + + // End group. + DO(output->WriteVarint32(kMessageSetItemEndTag)); + } +#undef DO + } + + return true; +} + +int WireFormat::ComputeUnknownFieldsSize( + const UnknownFieldSet& unknown_fields) { + int size = 0; + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + + for (int j = 0; j < field.varint_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_VARINT)); + size += io::CodedOutputStream::VarintSize64(field.varint(j)); + } + for (int j = 0; j < field.fixed32_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_FIXED32)); + size += sizeof(int32); + } + for (int j = 0; j < field.fixed64_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_FIXED64)); + size += sizeof(int64); + } + for (int j = 0; j < field.length_delimited_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_LENGTH_DELIMITED)); + size += io::CodedOutputStream::VarintSize32( + field.length_delimited(j).size()); + size += field.length_delimited(j).size(); + } + for (int j = 0; j < field.group_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_START_GROUP)); + size += ComputeUnknownFieldsSize(field.group(j)); + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_END_GROUP)); + } + } + + return size; +} + +int WireFormat::ComputeUnknownMessageSetItemsSize( + const UnknownFieldSet& unknown_fields) { + int size = 0; + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + + // The only unknown fields that are allowed to exist in a MessageSet are + // messages, which are length-delimited. + for (int j = 0; j < field.length_delimited_size(); j++) { + size += kMessageSetItemTagsSize; + size += io::CodedOutputStream::VarintSize32(field.number()); + size += io::CodedOutputStream::VarintSize32( + field.length_delimited(j).size()); + size += field.length_delimited(j).size(); + } + } + + return size; +} + +// =================================================================== + +bool WireFormat::ParseAndMergePartial(const Descriptor* descriptor, + io::CodedInputStream* input, + Message::Reflection* message_reflection) { + while(true) { + uint32 tag = input->ReadTag(); + if (tag == 0) { + // End of input. This is a valid place to end, so return true. + return true; + } + + if (GetTagWireType(tag) == WIRETYPE_END_GROUP) { + // Must be the end of the message. + return true; + } + + const FieldDescriptor* field = NULL; + + if (descriptor != NULL) { + int field_number = GetTagFieldNumber(tag); + field = descriptor->FindFieldByNumber(field_number); + + // If that failed, check if the field is an extension. + if (field == NULL && descriptor->IsExtensionNumber(field_number)) { + field = message_reflection->FindKnownExtensionByNumber(field_number); + } + + // If that failed, but we're a MessageSet, and this is the tag for a + // MessageSet item, then parse that. + if (field == NULL && + descriptor->options().message_set_wire_format() && + tag == kMessageSetItemStartTag) { + if (!ParseAndMergeMessageSetItem(input, message_reflection)) { + return false; + } + continue; // Skip ParseAndMergeField(); already taken care of. + } + } + + if (!ParseAndMergeField(tag, field, message_reflection, input)) { + return false; + } + } +} + +bool WireFormat::ParseAndMergeField( + uint32 tag, + const FieldDescriptor* field, // May be NULL for unknown + Message::Reflection* message_reflection, + io::CodedInputStream* input) { + if (field == NULL || + GetTagWireType(tag) != WireTypeForFieldType(field->type())) { + // We don't recognize this field. Either the field number is unknown + // or the wire type doesn't match. Put it in our unknown field set. + return SkipField(input, tag, message_reflection->MutableUnknownFields()); + } + + switch (field->type()) { +#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: { \ + CPPTYPE value; \ + if (!Read##TYPE_METHOD(input, &value)) return false; \ + if (field->is_repeated()) { \ + message_reflection->Add##CPPTYPE_METHOD(field, value); \ + } else { \ + message_reflection->Set##CPPTYPE_METHOD(field, value); \ + } \ + break; \ + } + + HANDLE_TYPE( INT32, Int32, int32, Int32) + HANDLE_TYPE( INT64, Int64, int64, Int64) + HANDLE_TYPE(SINT32, SInt32, int32, Int32) + HANDLE_TYPE(SINT64, SInt64, int64, Int64) + HANDLE_TYPE(UINT32, UInt32, uint32, UInt32) + HANDLE_TYPE(UINT64, UInt64, uint64, UInt64) + + HANDLE_TYPE( FIXED32, Fixed32, uint32, UInt32) + HANDLE_TYPE( FIXED64, Fixed64, uint64, UInt64) + HANDLE_TYPE(SFIXED32, SFixed32, int32, Int32) + HANDLE_TYPE(SFIXED64, SFixed64, int64, Int64) + + HANDLE_TYPE(FLOAT , Float , float , Float ) + HANDLE_TYPE(DOUBLE, Double, double, Double) + + HANDLE_TYPE(BOOL, Bool, bool, Bool) + + HANDLE_TYPE(STRING, String, string, String) + HANDLE_TYPE(BYTES, Bytes, string, String) + +#undef HANDLE_TYPE + + case FieldDescriptor::TYPE_ENUM: { + int value; + if (!ReadEnum(input, &value)) return false; + const EnumValueDescriptor* enum_value = + field->enum_type()->FindValueByNumber(value); + if (enum_value != NULL) { + if (field->is_repeated()) { + message_reflection->AddEnum(field, enum_value); + } else { + message_reflection->SetEnum(field, enum_value); + } + } else { + // The enum value is not one of the known values. Add it to the + // UnknownFieldSet. + int64 sign_extended_value = static_cast(value); + message_reflection->MutableUnknownFields() + ->AddField(GetTagFieldNumber(tag)) + ->add_varint(sign_extended_value); + } + break; + } + + + case FieldDescriptor::TYPE_GROUP: { + Message* sub_message; + if (field->is_repeated()) { + sub_message = message_reflection->AddMessage(field); + } else { + sub_message = message_reflection->MutableMessage(field); + } + + if (!ReadGroup(GetTagFieldNumber(tag), input, sub_message)) return false; + break; + } + + case FieldDescriptor::TYPE_MESSAGE: { + Message* sub_message; + if (field->is_repeated()) { + sub_message = message_reflection->AddMessage(field); + } else { + sub_message = message_reflection->MutableMessage(field); + } + + if (!ReadMessage(input, sub_message)) return false; + break; + } + } + + return true; +} + +bool WireFormat::ParseAndMergeMessageSetItem( + io::CodedInputStream* input, + Message::Reflection* message_reflection) { + // This method parses a group which should contain two fields: + // required int32 type_id = 2; + // required data message = 3; + + // Once we see a type_id, we'll construct a fake tag for this extension + // which is the tag it would have had under the proto2 extensions wire + // format. + uint32 fake_tag = 0; + + // Once we see a type_id, we'll look up the FieldDescriptor for the + // extension. + const FieldDescriptor* field = NULL; + + // If we see message data before the type_id, we'll append it to this so + // we can parse it later. This will probably never happen in practice, + // as no MessageSet encoder I know of writes the message before the type ID. + // But, it's technically valid so we should allow it. + // TODO(kenton): Use a Cord instead? Do I care? + string message_data; + + while (true) { + uint32 tag = input->ReadTag(); + if (tag == 0) return false; + + switch (tag) { + case kMessageSetTypeIdTag: { + uint32 type_id; + if (!input->ReadVarint32(&type_id)) return false; + fake_tag = MakeTag(type_id, WIRETYPE_LENGTH_DELIMITED); + field = message_reflection->FindKnownExtensionByNumber(type_id); + + if (!message_data.empty()) { + // We saw some message data before the type_id. Have to parse it + // now. + io::ArrayInputStream raw_input(message_data.data(), + message_data.size()); + io::CodedInputStream sub_input(&raw_input); + if (!ParseAndMergeField(fake_tag, field, message_reflection, + &sub_input)) { + return false; + } + message_data.clear(); + } + + break; + } + + case kMessageSetMessageTag: { + if (fake_tag == 0) { + // We haven't seen a type_id yet. Append this data to message_data. + string temp; + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (!input->ReadString(&temp, length)) return false; + message_data.append(temp); + } else { + // Already saw type_id, so we can parse this directly. + if (!ParseAndMergeField(fake_tag, field, message_reflection, input)) { + return false; + } + } + + break; + } + + case kMessageSetItemEndTag: { + return true; + } + + default: { + if (!SkipField(input, tag, NULL)) return false; + } + } + } +} + +// =================================================================== + +bool WireFormat::SerializeWithCachedSizes( + const Descriptor* descriptor, + const Message::Reflection* message_reflection, + int size, io::CodedOutputStream* output) { + int expected_endpoint = output->ByteCount() + size; + + vector fields; + message_reflection->ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + if (!SerializeFieldWithCachedSizes(fields[i], message_reflection, output)) { + return false; + } + } + + if (descriptor->options().message_set_wire_format()) { + if (!SerializeUnknownMessageSetItems( + message_reflection->GetUnknownFields(), output)) { + return false; + } + } else { + if (!SerializeUnknownFields( + message_reflection->GetUnknownFields(), output)) { + return false; + } + } + + GOOGLE_CHECK_EQ(output->ByteCount(), expected_endpoint) + << ": Protocol message serialized to a size different from what was " + "originally expected. Perhaps it was modified by another thread " + "during serialization?"; + + return true; +} + +bool WireFormat::SerializeFieldWithCachedSizes( + const FieldDescriptor* field, + const Message::Reflection* message_reflection, + io::CodedOutputStream* output) { + if (field->is_extension() && + field->containing_type()->options().message_set_wire_format() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + !field->is_repeated()) { + return SerializeMessageSetItemWithCachedSizes( + field, message_reflection, output); + } + + int count = 0; + + if (field->is_repeated()) { + count = message_reflection->FieldSize(field); + } else if (message_reflection->HasField(field)) { + count = 1; + } + + for (int j = 0; j < count; j++) { + switch (field->type()) { +#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: \ + if (!Write##TYPE_METHOD( \ + field->number(), \ + field->is_repeated() ? \ + message_reflection->GetRepeated##CPPTYPE_METHOD(field, j) : \ + message_reflection->Get##CPPTYPE_METHOD(field), \ + output)) { \ + return false; \ + } \ + break; + + HANDLE_TYPE( INT32, Int32, Int32) + HANDLE_TYPE( INT64, Int64, Int64) + HANDLE_TYPE(SINT32, SInt32, Int32) + HANDLE_TYPE(SINT64, SInt64, Int64) + HANDLE_TYPE(UINT32, UInt32, UInt32) + HANDLE_TYPE(UINT64, UInt64, UInt64) + + HANDLE_TYPE( FIXED32, Fixed32, UInt32) + HANDLE_TYPE( FIXED64, Fixed64, UInt64) + HANDLE_TYPE(SFIXED32, SFixed32, Int32) + HANDLE_TYPE(SFIXED64, SFixed64, Int64) + + HANDLE_TYPE(FLOAT , Float , Float ) + HANDLE_TYPE(DOUBLE, Double, Double) + + HANDLE_TYPE(BOOL, Bool, Bool) + + HANDLE_TYPE(GROUP , Group , Message) + HANDLE_TYPE(MESSAGE, Message, Message) +#undef HANDLE_TYPE + + case FieldDescriptor::TYPE_ENUM: { + const EnumValueDescriptor* value = field->is_repeated() ? + message_reflection->GetRepeatedEnum(field, j) : + message_reflection->GetEnum(field); + if (!WriteEnum(field->number(), value->number(), output)) return false; + break; + } + + // Handle strings separately so that we can get string references + // instead of copying. + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: { + string scratch; + const string& value = field->is_repeated() ? + message_reflection->GetRepeatedStringReference(field, j, &scratch) : + message_reflection->GetStringReference(field, &scratch); + if (!WriteString(field->number(), value, output)) return false; + break; + } + } + } + + return true; +} + +bool WireFormat::SerializeMessageSetItemWithCachedSizes( + const FieldDescriptor* field, + const Message::Reflection* message_reflection, + io::CodedOutputStream* output) { + // Start group. + if (!output->WriteVarint32(kMessageSetItemStartTag)) return false; + + // Write type ID. + if (!output->WriteVarint32(kMessageSetTypeIdTag)) return false; + if (!output->WriteVarint32(field->number())) return false; + + // Write message. + if (!output->WriteVarint32(kMessageSetMessageTag)) return false; + + const Message& sub_message = message_reflection->GetMessage(field); + if (!output->WriteVarint32(sub_message.GetCachedSize())) return false; + if (!sub_message.SerializeWithCachedSizes(output)) return false; + + // End group. + if (!output->WriteVarint32(kMessageSetItemEndTag)) return false; + + return true; +} + +// =================================================================== + +int WireFormat::ByteSize( + const Descriptor* descriptor, + const Message::Reflection* message_reflection) { + int our_size = 0; + + vector fields; + message_reflection->ListFields(&fields); + for (int i = 0; i < fields.size(); i++) { + our_size += FieldByteSize(fields[i], message_reflection); + } + + if (descriptor->options().message_set_wire_format()) { + our_size += ComputeUnknownMessageSetItemsSize( + message_reflection->GetUnknownFields()); + } else { + our_size += ComputeUnknownFieldsSize( + message_reflection->GetUnknownFields()); + } + + return our_size; +} + +int WireFormat::FieldByteSize( + const FieldDescriptor* field, + const Message::Reflection* message_reflection) { + if (field->is_extension() && + field->containing_type()->options().message_set_wire_format() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + !field->is_repeated()) { + return MessageSetItemByteSize(field, message_reflection); + } + + int our_size = 0; + + int count = 0; + + if (field->is_repeated()) { + count = message_reflection->FieldSize(field); + } else if (message_reflection->HasField(field)) { + count = 1; + } + + our_size += count * TagSize(field->number(), field->type()); + + switch (field->type()) { +#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: \ + if (field->is_repeated()) { \ + for (int j = 0; j < count; j++) { \ + our_size += TYPE_METHOD##Size( \ + message_reflection->GetRepeated##CPPTYPE_METHOD(field, j)); \ + } \ + } else { \ + our_size += TYPE_METHOD##Size( \ + message_reflection->Get##CPPTYPE_METHOD(field)); \ + } \ + break; + +#define HANDLE_FIXED_TYPE(TYPE, TYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: \ + our_size += count * k##TYPE_METHOD##Size; \ + break; + + HANDLE_TYPE( INT32, Int32, Int32) + HANDLE_TYPE( INT64, Int64, Int64) + HANDLE_TYPE(SINT32, SInt32, Int32) + HANDLE_TYPE(SINT64, SInt64, Int64) + HANDLE_TYPE(UINT32, UInt32, UInt32) + HANDLE_TYPE(UINT64, UInt64, UInt64) + + HANDLE_FIXED_TYPE( FIXED32, Fixed32) + HANDLE_FIXED_TYPE( FIXED64, Fixed64) + HANDLE_FIXED_TYPE(SFIXED32, SFixed32) + HANDLE_FIXED_TYPE(SFIXED64, SFixed64) + + HANDLE_FIXED_TYPE(FLOAT , Float ) + HANDLE_FIXED_TYPE(DOUBLE, Double) + + HANDLE_FIXED_TYPE(BOOL, Bool) + + HANDLE_TYPE(GROUP , Group , Message) + HANDLE_TYPE(MESSAGE, Message, Message) +#undef HANDLE_TYPE +#undef HANDLE_FIXED_TYPE + + case FieldDescriptor::TYPE_ENUM: { + if (field->is_repeated()) { + for (int j = 0; j < count; j++) { + our_size += EnumSize( + message_reflection->GetRepeatedEnum(field, j)->number()); + } + } else { + our_size += EnumSize( + message_reflection->GetEnum(field)->number()); + } + break; + } + + // Handle strings separately so that we can get string references + // instead of copying. + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: { + for (int j = 0; j < count; j++) { + string scratch; + const string& value = field->is_repeated() ? + message_reflection->GetRepeatedStringReference(field, j, &scratch) : + message_reflection->GetStringReference(field, &scratch); + our_size += StringSize(value); + } + break; + } + } + + return our_size; +} + +int WireFormat::MessageSetItemByteSize( + const FieldDescriptor* field, + const Message::Reflection* message_reflection) { + int our_size = kMessageSetItemTagsSize; + + // type_id + our_size += io::CodedOutputStream::VarintSize32(field->number()); + + // message + const Message& sub_message = message_reflection->GetMessage(field); + int message_size = sub_message.ByteSize(); + + our_size += io::CodedOutputStream::VarintSize32(message_size); + our_size += message_size; + + return our_size; +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/wire_format.h b/src/google/protobuf/wire_format.h new file mode 100644 index 00000000..d59f3fc1 --- /dev/null +++ b/src/google/protobuf/wire_format.h @@ -0,0 +1,446 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// atenasio@google.com (Chris Atenasio) (ZigZag transform) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_WIRE_FORMAT_H__ +#define GOOGLE_PROTOBUF_WIRE_FORMAT_H__ + +#include +#include // Message::Reflection +#include + +namespace google { + +namespace protobuf { + namespace io { + class CodedInputStream; // coded_stream.h + class CodedOutputStream; // coded_stream.h + } + class UnknownFieldSet; // unknown_field_set.h +} + +namespace protobuf { +namespace internal { + +// This class is for internal use by the protocol buffer library and by +// protocol-complier-generated message classes. It must not be called +// directly by clients. +// +// This class contains helpers for implementing the binary protocol buffer +// wire format. These helpers are called primarily by generated code. The +// class also contains reflection-based implementations of the wire format. +// +// This class is really a namespace that contains only static methods. +class LIBPROTOBUF_EXPORT WireFormat { + public: + // These procedures can be used to implement the methods of Message which + // handle parsing and serialization of the protocol buffer wire format + // using only the Message::Reflection interface. When you ask the protocol + // compiler to optimize for code size rather than speed, it will implement + // those methods in terms of these procedures. Of course, these are much + // slower than the specialized implementations which the protocol compiler + // generates when told to optimize for speed. + + // Read a message in protocol buffer wire format. + // + // This procedure reads either to the end of the input stream or through + // a WIRETYPE_END_GROUP tag ending the message, whichever comes first. + // It returns false if the input is invalid. + // + // Required fields are NOT checked by this method. You must call + // IsInitialized() on the resulting message yourself. + static bool ParseAndMergePartial(const Descriptor* descriptor, + io::CodedInputStream* input, + Message::Reflection* message_reflection); + + // Serialize a message in protocol buffer wire format. + // + // Any embedded messages within the message must have their correct sizes + // cached. However, the top-level message need not; its size is passed as + // a parameter to this procedure. + // + // These return false iff the underlying stream returns a write error. + static bool SerializeWithCachedSizes( + const Descriptor* descriptor, + const Message::Reflection* message_reflection, + int size, io::CodedOutputStream* output); + + // Implements Message::ByteSize() via reflection. WARNING: The result + // of this method is *not* cached anywhere. However, all embedded messages + // will have their ByteSize() methods called, so their sizes will be cached. + // Therefore, calling this method is sufficient to allow you to call + // WireFormat::SerializeWithCachedSizes() on the same object. + static int ByteSize(const Descriptor* descriptor, + const Message::Reflection* message_reflection); + + // ----------------------------------------------------------------- + // Helpers for dealing with unknown fields + + // Skips a field value of the given WireType. The input should start + // positioned immediately after the tag. If unknown_fields is non-NULL, + // the contents of the field will be added to it. + static bool SkipField(io::CodedInputStream* input, uint32 tag, + UnknownFieldSet* unknown_fields); + + // Reads and ignores a message from the input. If unknown_fields is non-NULL, + // the contents will be added to it. + static bool SkipMessage(io::CodedInputStream* input, + UnknownFieldSet* unknown_fields); + + // Write the contents of an UnknownFieldSet to the output. + static bool SerializeUnknownFields(const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output); + + // Same thing except for messages that have the message_set_wire_format + // option. + static bool SerializeUnknownMessageSetItems( + const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output); + + // Compute the size of the UnknownFieldSet on the wire. + static int ComputeUnknownFieldsSize(const UnknownFieldSet& unknown_fields); + + // Same thing except for messages that have the message_set_wire_format + // option. + static int ComputeUnknownMessageSetItemsSize( + const UnknownFieldSet& unknown_fields); + + // ----------------------------------------------------------------- + // Helper constants and functions related to the format. These are + // mostly meant for internal and generated code to use. + + // The wire format is composed of a sequence of tag/value pairs, each + // of which contains the value of one field (or one element of a repeated + // field). Each tag is encoded as a varint. The lower bits of the tag + // identify its wire type, which specifies the format of the data to follow. + // The rest of the bits contain the field number. Each type of field (as + // declared by FieldDescriptor::Type, in descriptor.h) maps to one of + // these wire types. Immediately following each tag is the field's value, + // encoded in the format specified by the wire type. Because the tag + // identifies the encoding of this data, it is possible to skip + // unrecognized fields for forwards compatibility. + + enum WireType { + WIRETYPE_VARINT = 0, + WIRETYPE_FIXED64 = 1, + WIRETYPE_LENGTH_DELIMITED = 2, + WIRETYPE_START_GROUP = 3, + WIRETYPE_END_GROUP = 4, + WIRETYPE_FIXED32 = 5, + }; + + static inline WireType WireTypeForFieldType(FieldDescriptor::Type type) { + return kWireTypeForFieldType[type]; + } + + // Number of bits in a tag which identify the wire type. + static const int kTagTypeBits = 3; + // Mask for those bits. + static const uint32 kTagTypeMask = (1 << kTagTypeBits) - 1; + + // Helper functions for encoding and decoding tags. (Inlined below.) + static uint32 MakeTag(const FieldDescriptor* field); + static uint32 MakeTag(int field_number, WireType type); + static WireType GetTagWireType(uint32 tag); + static int GetTagFieldNumber(uint32 tag); + + // Helper functions for converting between floats/doubles and IEEE-754 + // uint32s/uint64s so that they can be written. (Assumes your platform + // uses IEEE-754 floats.) + static uint32 EncodeFloat(float value); + static float DecodeFloat(uint32 value); + static uint64 EncodeDouble(double value); + static double DecodeDouble(uint64 value); + + // Helper functions for mapping signed integers to unsigned integers in + // such a way that numbers with small magnitudes will encode to smaller + // varints. If you simply static_cast a negative number to an unsigned + // number and varint-encode it, it will always take 10 bytes, defeating + // the purpose of varint. So, for the "sint32" and "sint64" field types, + // we ZigZag-encode the values. + static uint32 ZigZagEncode32(int32 n); + static int32 ZigZagDecode32(uint32 n); + static uint64 ZigZagEncode64(int64 n); + static int64 ZigZagDecode64(uint64 n); + + // Parse a single field. The input should start out positioned immidately + // after the tag. + static bool ParseAndMergeField( + uint32 tag, + const FieldDescriptor* field, // May be NULL for unknown + Message::Reflection* message_reflection, + io::CodedInputStream* input); + + // Serialize a single field. + static bool SerializeFieldWithCachedSizes( + const FieldDescriptor* field, // Cannot be NULL + const Message::Reflection* message_reflection, + io::CodedOutputStream* output); + + // Compute size of a single field. If the field is a message type, this + // will call ByteSize() for the embedded message, insuring that it caches + // its size. + static int FieldByteSize( + const FieldDescriptor* field, // Cannot be NULL + const Message::Reflection* message_reflection); + + // ================================================================= + // Methods for reading/writing individual field. The implementations + // of these methods are defined in wire_format_inl.h; you must #include + // that file to use these. + +// Avoid ugly line wrapping +#define input io::CodedInputStream* input +#define output io::CodedOutputStream* output +#define field_number int field_number +#define INL GOOGLE_ATTRIBUTE_ALWAYS_INLINE + + // Read fields, not including tags. The assumption is that you already + // read the tag to determine what field to read. + static inline bool ReadInt32 (input, int32* value); + static inline bool ReadInt64 (input, int64* value); + static inline bool ReadUInt32 (input, uint32* value); + static inline bool ReadUInt64 (input, uint64* value); + static inline bool ReadSInt32 (input, int32* value); + static inline bool ReadSInt64 (input, int64* value); + static inline bool ReadFixed32 (input, uint32* value); + static inline bool ReadFixed64 (input, uint64* value); + static inline bool ReadSFixed32(input, int32* value); + static inline bool ReadSFixed64(input, int64* value); + static inline bool ReadFloat (input, float* value); + static inline bool ReadDouble (input, double* value); + static inline bool ReadBool (input, bool* value); + static inline bool ReadEnum (input, int* value); + + static inline bool ReadString(input, string* value); + static inline bool ReadBytes (input, string* value); + + static inline bool ReadGroup (field_number, input, Message* value); + static inline bool ReadMessage(input, Message* value); + + // Like above, but de-virtualize the call to MergePartialFromCodedStream(). + // The pointer must point at an instance of MessageType, *not* a subclass (or + // the subclass must not override MergePartialFromCodedStream()). + template + static inline bool ReadGroupNoVirtual(field_number, input, + MessageType* value); + template + static inline bool ReadMessageNoVirtual(input, MessageType* value); + + // Write a tag. The Write*() functions automatically include the tag, so + // normally there's no need to call this. + static inline bool WriteTag(field_number, WireType type, output) INL; + + // Write fields, including tags. + static inline bool WriteInt32 (field_number, int32 value, output) INL; + static inline bool WriteInt64 (field_number, int64 value, output) INL; + static inline bool WriteUInt32 (field_number, uint32 value, output) INL; + static inline bool WriteUInt64 (field_number, uint64 value, output) INL; + static inline bool WriteSInt32 (field_number, int32 value, output) INL; + static inline bool WriteSInt64 (field_number, int64 value, output) INL; + static inline bool WriteFixed32 (field_number, uint32 value, output) INL; + static inline bool WriteFixed64 (field_number, uint64 value, output) INL; + static inline bool WriteSFixed32(field_number, int32 value, output) INL; + static inline bool WriteSFixed64(field_number, int64 value, output) INL; + static inline bool WriteFloat (field_number, float value, output) INL; + static inline bool WriteDouble (field_number, double value, output) INL; + static inline bool WriteBool (field_number, bool value, output) INL; + static inline bool WriteEnum (field_number, int value, output) INL; + + static inline bool WriteString(field_number, const string& value, output) INL; + static inline bool WriteBytes (field_number, const string& value, output) INL; + + static inline bool WriteGroup(field_number, const Message& value, output) INL; + static inline bool WriteMessage( + field_number, const Message& value, output) INL; + + // Like above, but de-virtualize the call to SerializeWithCachedSizes(). The + // pointer must point at an instance of MessageType, *not* a subclass (or + // the subclass must not override SerializeWithCachedSizes()). + template + static inline bool WriteGroupNoVirtual( + field_number, const MessageType& value, output) INL; + template + static inline bool WriteMessageNoVirtual( + field_number, const MessageType& value, output) INL; + + // Compute the byte size of a tag. For groups, this includes both the start + // and end tags. + static inline int TagSize(field_number, FieldDescriptor::Type type); + + // Compute the byte size of a field. The XxSize() functions do NOT include + // the tag, so you must also call TagSize(). (This is because, for repeated + // fields, you should only call TagSize() once and multiply it by the element + // count, but you may have to call XxSize() for each individual element.) + static inline int Int32Size ( int32 value); + static inline int Int64Size ( int64 value); + static inline int UInt32Size (uint32 value); + static inline int UInt64Size (uint64 value); + static inline int SInt32Size ( int32 value); + static inline int SInt64Size ( int64 value); + static inline int EnumSize ( int value); + + // These types always have the same size. + static const int kFixed32Size = 4; + static const int kFixed64Size = 8; + static const int kSFixed32Size = 4; + static const int kSFixed64Size = 8; + static const int kFloatSize = 4; + static const int kDoubleSize = 8; + static const int kBoolSize = 1; + + static inline int StringSize(const string& value); + static inline int BytesSize (const string& value); + + static inline int GroupSize (const Message& value); + static inline int MessageSize(const Message& value); + + // Like above, but de-virtualize the call to ByteSize(). The + // pointer must point at an instance of MessageType, *not* a subclass (or + // the subclass must not override ByteSize()). + template + static inline int GroupSizeNoVirtual (const MessageType& value); + template + static inline int MessageSizeNoVirtual(const MessageType& value); + +#undef input +#undef output +#undef field_number +#undef INL + + private: + static const WireType kWireTypeForFieldType[]; + + // Parse/serialize a MessageSet::Item group. Used with messages that use + // opion message_set_wire_format = true. + static bool ParseAndMergeMessageSetItem( + io::CodedInputStream* input, + Message::Reflection* message_reflection); + static bool SerializeMessageSetItemWithCachedSizes( + const FieldDescriptor* field, + const Message::Reflection* message_reflection, + io::CodedOutputStream* output); + static int MessageSetItemByteSize( + const FieldDescriptor* field, + const Message::Reflection* message_reflection); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WireFormat); +}; + +// inline methods ==================================================== + +// This macro does the same thing as WireFormat::MakeTag(), but the +// result is usable as a compile-time constant, which makes it usable +// as a switch case or a template input. WireFormat::MakeTag() is more +// type-safe, though, so prefer it if possible. +#define GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(FIELD_NUMBER, TYPE) \ + static_cast( \ + ((FIELD_NUMBER) << ::google::protobuf::internal::WireFormat::kTagTypeBits) | (TYPE)) + +inline uint32 WireFormat::MakeTag(const FieldDescriptor* field) { + return MakeTag(field->number(), WireTypeForFieldType(field->type())); +} + +inline uint32 WireFormat::MakeTag(int field_number, WireType type) { + return GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(field_number, type); +} + +inline WireFormat::WireType WireFormat::GetTagWireType(uint32 tag) { + return static_cast(tag & kTagTypeMask); +} + +inline int WireFormat::GetTagFieldNumber(uint32 tag) { + return static_cast(tag >> kTagTypeBits); +} + +inline uint32 WireFormat::EncodeFloat(float value) { + union {float f; uint32 i;}; + f = value; + return i; +} + +inline float WireFormat::DecodeFloat(uint32 value) { + union {float f; uint32 i;}; + i = value; + return f; +} + +inline uint64 WireFormat::EncodeDouble(double value) { + union {double f; uint64 i;}; + f = value; + return i; +} + +inline double WireFormat::DecodeDouble(uint64 value) { + union {double f; uint64 i;}; + i = value; + return f; +} + +// ZigZag Transform: Encodes signed integers so that they can be +// effectively used with varint encoding. +// +// varint operates on unsigned integers, encoding smaller numbers into +// fewer bytes. If you try to use it on a signed integer, it will treat +// this number as a very large unsigned integer, which means that even +// small signed numbers like -1 will take the maximum number of bytes +// (10) to encode. ZigZagEncode() maps signed integers to unsigned +// in such a way that those with a small absolute value will have smaller +// encoded values, making them appropriate for encoding using varint. +// +// int32 -> uint32 +// ------------------------- +// 0 -> 0 +// -1 -> 1 +// 1 -> 2 +// -2 -> 3 +// ... -> ... +// 2147483647 -> 4294967294 +// -2147483648 -> 4294967295 +// +// >> encode >> +// << decode << + +inline uint32 WireFormat::ZigZagEncode32(int32 n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); +} + +inline int32 WireFormat::ZigZagDecode32(uint32 n) { + return (n >> 1) ^ -static_cast(n & 1); +} + +inline uint64 WireFormat::ZigZagEncode64(int64 n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); +} + +inline int64 WireFormat::ZigZagDecode64(uint64 n) { + return (n >> 1) ^ -static_cast(n & 1); +} + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_WIRE_FORMAT_H__ diff --git a/src/google/protobuf/wire_format_inl.h b/src/google/protobuf/wire_format_inl.h new file mode 100644 index 00000000..d8cdd8d6 --- /dev/null +++ b/src/google/protobuf/wire_format_inl.h @@ -0,0 +1,371 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_WIRE_FORMAT_INL_H__ +#define GOOGLE_PROTOBUF_WIRE_FORMAT_INL_H__ + +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace internal { + +inline bool WireFormat::ReadInt32(io::CodedInputStream* input, int32* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadInt64(io::CodedInputStream* input, int64* value) { + uint64 temp; + if (!input->ReadVarint64(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadUInt32(io::CodedInputStream* input, uint32* value) { + return input->ReadVarint32(value); +} +inline bool WireFormat::ReadUInt64(io::CodedInputStream* input, uint64* value) { + return input->ReadVarint64(value); +} +inline bool WireFormat::ReadSInt32(io::CodedInputStream* input, int32* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = ZigZagDecode32(temp); + return true; +} +inline bool WireFormat::ReadSInt64(io::CodedInputStream* input, int64* value) { + uint64 temp; + if (!input->ReadVarint64(&temp)) return false; + *value = ZigZagDecode64(temp); + return true; +} +inline bool WireFormat::ReadFixed32(io::CodedInputStream* input, + uint32* value) { + return input->ReadLittleEndian32(value); +} +inline bool WireFormat::ReadFixed64(io::CodedInputStream* input, + uint64* value) { + return input->ReadLittleEndian64(value); +} +inline bool WireFormat::ReadSFixed32(io::CodedInputStream* input, + int32* value) { + uint32 temp; + if (!input->ReadLittleEndian32(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadSFixed64(io::CodedInputStream* input, + int64* value) { + uint64 temp; + if (!input->ReadLittleEndian64(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadFloat(io::CodedInputStream* input, float* value) { + uint32 temp; + if (!input->ReadLittleEndian32(&temp)) return false; + *value = DecodeFloat(temp); + return true; +} +inline bool WireFormat::ReadDouble(io::CodedInputStream* input, double* value) { + uint64 temp; + if (!input->ReadLittleEndian64(&temp)) return false; + *value = DecodeDouble(temp); + return true; +} +inline bool WireFormat::ReadBool(io::CodedInputStream* input, bool* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = temp != 0; + return true; +} +inline bool WireFormat::ReadEnum(io::CodedInputStream* input, int* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = static_cast(temp); + return true; +} + +inline bool WireFormat::ReadString(io::CodedInputStream* input, string* value) { + // WARNING: In wire_format.cc, both strings and bytes are handled by + // ReadString() to avoid code duplication. If the implementations become + // different, you will need to update that usage. + uint32 length; + if (!input->ReadVarint32(&length)) return false; + return input->ReadString(value, length); +} +inline bool WireFormat::ReadBytes(io::CodedInputStream* input, string* value) { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + return input->ReadString(value, length); +} + + +inline bool WireFormat::ReadGroup(int field_number, io::CodedInputStream* input, + Message* value) { + if (!input->IncrementRecursionDepth()) return false; + if (!value->MergePartialFromCodedStream(input)) return false; + input->DecrementRecursionDepth(); + // Make sure the last thing read was an end tag for this group. + if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) { + return false; + } + return true; +} +inline bool WireFormat::ReadMessage(io::CodedInputStream* input, Message* value) { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (!input->IncrementRecursionDepth()) return false; + io::CodedInputStream::Limit limit = input->PushLimit(length); + if (!value->MergePartialFromCodedStream(input)) return false; + // Make sure that parsing stopped when the limit was hit, not at an endgroup + // tag. + if (!input->ConsumedEntireMessage()) return false; + input->PopLimit(limit); + input->DecrementRecursionDepth(); + return true; +} + +template +inline bool WireFormat::ReadGroupNoVirtual(int field_number, + io::CodedInputStream* input, + MessageType* value) { + if (!input->IncrementRecursionDepth()) return false; + if (!value->MessageType::MergePartialFromCodedStream(input)) return false; + input->DecrementRecursionDepth(); + // Make sure the last thing read was an end tag for this group. + if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) { + return false; + } + return true; +} +template +inline bool WireFormat::ReadMessageNoVirtual(io::CodedInputStream* input, + MessageType* value) { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (!input->IncrementRecursionDepth()) return false; + io::CodedInputStream::Limit limit = input->PushLimit(length); + if (!value->MessageType::MergePartialFromCodedStream(input)) return false; + // Make sure that parsing stopped when the limit was hit, not at an endgroup + // tag. + if (!input->ConsumedEntireMessage()) return false; + input->PopLimit(limit); + input->DecrementRecursionDepth(); + return true; +} + +// =================================================================== + +inline bool WireFormat::WriteTag(int field_number, WireType type, + io::CodedOutputStream* output) { + return output->WriteTag(MakeTag(field_number, type)); +} + +inline bool WireFormat::WriteInt32(int field_number, int32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32SignExtended(value); +} +inline bool WireFormat::WriteInt64(int field_number, int64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint64(static_cast(value)); +} +inline bool WireFormat::WriteUInt32(int field_number, uint32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32(value); +} +inline bool WireFormat::WriteUInt64(int field_number, uint64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint64(value); +} +inline bool WireFormat::WriteSInt32(int field_number, int32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32(ZigZagEncode32(value)); +} +inline bool WireFormat::WriteSInt64(int field_number, int64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint64(ZigZagEncode64(value)); +} +inline bool WireFormat::WriteFixed32(int field_number, uint32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED32, output) && + output->WriteLittleEndian32(value); +} +inline bool WireFormat::WriteFixed64(int field_number, uint64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED64, output) && + output->WriteLittleEndian64(value); +} +inline bool WireFormat::WriteSFixed32(int field_number, int32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED32, output) && + output->WriteLittleEndian32(static_cast(value)); +} +inline bool WireFormat::WriteSFixed64(int field_number, int64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED64, output) && + output->WriteLittleEndian64(static_cast(value)); +} +inline bool WireFormat::WriteFloat(int field_number, float value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED32, output) && + output->WriteLittleEndian32(EncodeFloat(value)); +} +inline bool WireFormat::WriteDouble(int field_number, double value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED64, output) && + output->WriteLittleEndian64(EncodeDouble(value)); +} +inline bool WireFormat::WriteBool(int field_number, bool value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32(value ? 1 : 0); +} +inline bool WireFormat::WriteEnum(int field_number, int value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32SignExtended(value); +} + +inline bool WireFormat::WriteString(int field_number, const string& value, + io::CodedOutputStream* output) { + // WARNING: In wire_format.cc, both strings and bytes are handled by + // WriteString() to avoid code duplication. If the implementations become + // different, you will need to update that usage. + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.size()) && + output->WriteString(value); +} +inline bool WireFormat::WriteBytes(int field_number, const string& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.size()) && + output->WriteString(value); +} + + +inline bool WireFormat::WriteGroup(int field_number, const Message& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_START_GROUP, output) && + value.SerializeWithCachedSizes(output) && + WriteTag(field_number, WIRETYPE_END_GROUP, output); +} +inline bool WireFormat::WriteMessage(int field_number, const Message& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.GetCachedSize()) && + value.SerializeWithCachedSizes(output); +} + +template +inline bool WireFormat::WriteGroupNoVirtual( + int field_number, const MessageType& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_START_GROUP, output) && + value.MessageType::SerializeWithCachedSizes(output) && + WriteTag(field_number, WIRETYPE_END_GROUP, output); +} +template +inline bool WireFormat::WriteMessageNoVirtual( + int field_number, const MessageType& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.MessageType::GetCachedSize()) && + value.MessageType::SerializeWithCachedSizes(output); +} + +// =================================================================== + +inline int WireFormat::TagSize(int field_number, FieldDescriptor::Type type) { + int result = io::CodedOutputStream::VarintSize32( + field_number << kTagTypeBits); + if (type == FieldDescriptor::TYPE_GROUP) { + // Groups have both a start and an end tag. + return result * 2; + } else { + return result; + } +} + +inline int WireFormat::Int32Size(int32 value) { + return io::CodedOutputStream::VarintSize32SignExtended(value); +} +inline int WireFormat::Int64Size(int64 value) { + return io::CodedOutputStream::VarintSize64(static_cast(value)); +} +inline int WireFormat::UInt32Size(uint32 value) { + return io::CodedOutputStream::VarintSize32(value); +} +inline int WireFormat::UInt64Size(uint64 value) { + return io::CodedOutputStream::VarintSize64(value); +} +inline int WireFormat::SInt32Size(int32 value) { + return io::CodedOutputStream::VarintSize32(ZigZagEncode32(value)); +} +inline int WireFormat::SInt64Size(int64 value) { + return io::CodedOutputStream::VarintSize64(ZigZagEncode64(value)); +} +inline int WireFormat::EnumSize(int value) { + return io::CodedOutputStream::VarintSize32SignExtended(value); +} + +inline int WireFormat::StringSize(const string& value) { + return io::CodedOutputStream::VarintSize32(value.size()) + + value.size(); +} +inline int WireFormat::BytesSize(const string& value) { + return io::CodedOutputStream::VarintSize32(value.size()) + + value.size(); +} + + +inline int WireFormat::GroupSize(const Message& value) { + return value.ByteSize(); +} +inline int WireFormat::MessageSize(const Message& value) { + int size = value.ByteSize(); + return io::CodedOutputStream::VarintSize32(size) + size; +} + +template +inline int WireFormat::GroupSizeNoVirtual(const MessageType& value) { + return value.MessageType::ByteSize(); +} +template +inline int WireFormat::MessageSizeNoVirtual(const MessageType& value) { + int size = value.MessageType::ByteSize(); + return io::CodedOutputStream::VarintSize32(size) + size; +} + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_WIRE_FORMAT_INL_H__ diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc new file mode 100644 index 00000000..7430786c --- /dev/null +++ b/src/google/protobuf/wire_format_unittest.cc @@ -0,0 +1,526 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { +namespace { + +TEST(WireFormatTest, MaxFieldNumber) { + // Make sure the max field number constant is accurate. + EXPECT_EQ((1 << (32 - WireFormat::kTagTypeBits)) - 1, + FieldDescriptor::kMaxNumber); +} + +TEST(WireFormatTest, Parse) { + unittest::TestAllTypes source, dest; + string data; + + // Serialize using the generated code. + TestUtil::SetAllFields(&source); + source.SerializeToString(&data); + + // Parse using WireFormat. + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + WireFormat::ParseAndMergePartial(unittest::TestAllTypes::descriptor(), + &input, dest.GetReflection()); + + // Check. + TestUtil::ExpectAllFieldsSet(dest); +} + +TEST(WireFormatTest, ParseExtensions) { + unittest::TestAllExtensions source, dest; + string data; + + // Serialize using the generated code. + TestUtil::SetAllExtensions(&source); + source.SerializeToString(&data); + + // Parse using WireFormat. + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + WireFormat::ParseAndMergePartial(unittest::TestAllExtensions::descriptor(), + &input, dest.GetReflection()); + + // Check. + TestUtil::ExpectAllExtensionsSet(dest); +} + +TEST(WireFormatTest, ByteSize) { + unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + + EXPECT_EQ(message.ByteSize(), + WireFormat::ByteSize(unittest::TestAllTypes::descriptor(), + message.GetReflection())); + message.Clear(); + EXPECT_EQ(0, message.ByteSize()); + EXPECT_EQ(0, WireFormat::ByteSize(unittest::TestAllTypes::descriptor(), + message.GetReflection())); +} + +TEST(WireFormatTest, ByteSizeExtensions) { + unittest::TestAllExtensions message; + TestUtil::SetAllExtensions(&message); + + EXPECT_EQ(message.ByteSize(), + WireFormat::ByteSize(unittest::TestAllExtensions::descriptor(), + message.GetReflection())); + message.Clear(); + EXPECT_EQ(0, message.ByteSize()); + EXPECT_EQ(0, WireFormat::ByteSize(unittest::TestAllExtensions::descriptor(), + message.GetReflection())); +} + +TEST(WireFormatTest, Serialize) { + unittest::TestAllTypes message; + string generated_data; + string dynamic_data; + + TestUtil::SetAllFields(&message); + int size = message.ByteSize(); + + // Serialize using the generated code. + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message.SerializeWithCachedSizes(&output); + } + + // Serialize using WireFormat. + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes( + unittest::TestAllTypes::descriptor(), + message.GetReflection(), size, &output); + } + + // Should be the same. + // Don't use EXPECT_EQ here because we're comparing raw binary data and + // we really don't want it dumped to stdout on failure. + EXPECT_TRUE(dynamic_data == generated_data); +} + +TEST(WireFormatTest, SerializeExtensions) { + unittest::TestAllExtensions message; + string generated_data; + string dynamic_data; + + TestUtil::SetAllExtensions(&message); + int size = message.ByteSize(); + + // Serialize using the generated code. + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message.SerializeWithCachedSizes(&output); + } + + // Serialize using WireFormat. + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes( + unittest::TestAllExtensions::descriptor(), + message.GetReflection(), size, &output); + } + + // Should be the same. + // Don't use EXPECT_EQ here because we're comparing raw binary data and + // we really don't want it dumped to stdout on failure. + EXPECT_TRUE(dynamic_data == generated_data); +} + +TEST(WireFormatTest, SerializeFieldsAndExtensions) { + unittest::TestFieldOrderings message; + string generated_data; + string dynamic_data; + + TestUtil::SetAllFieldsAndExtensions(&message); + int size = message.ByteSize(); + + // Serialize using the generated code. + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message.SerializeWithCachedSizes(&output); + } + + // Serialize using WireFormat. + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes( + unittest::TestFieldOrderings::descriptor(), + message.GetReflection(), size, &output); + } + + // Should be the same. + // Don't use EXPECT_EQ here because we're comparing raw binary data and + // we really don't want it dumped to stdout on failure. + EXPECT_TRUE(dynamic_data == generated_data); + + // Should output in canonical order. + TestUtil::ExpectAllFieldsAndExtensionsInOrder(dynamic_data); + TestUtil::ExpectAllFieldsAndExtensionsInOrder(generated_data); +} + +const int kUnknownTypeId = 1550055; + +TEST(WireFormatTest, SerializeMessageSet) { + // Set up a TestMessageSet with two known messages and an unknown one. + unittest::TestMessageSet message_set; + message_set.MutableExtension( + unittest::TestMessageSetExtension1::message_set_extension)->set_i(123); + message_set.MutableExtension( + unittest::TestMessageSetExtension2::message_set_extension)->set_str("foo"); + message_set.mutable_unknown_fields()->AddField(kUnknownTypeId) + ->add_length_delimited("bar"); + + string data; + ASSERT_TRUE(message_set.SerializeToString(&data)); + + // Parse back using RawMessageSet and check the contents. + unittest::RawMessageSet raw; + ASSERT_TRUE(raw.ParseFromString(data)); + + EXPECT_EQ(0, raw.unknown_fields().field_count()); + + ASSERT_EQ(3, raw.item_size()); + EXPECT_EQ( + unittest::TestMessageSetExtension1::descriptor()->extension(0)->number(), + raw.item(0).type_id()); + EXPECT_EQ( + unittest::TestMessageSetExtension2::descriptor()->extension(0)->number(), + raw.item(1).type_id()); + EXPECT_EQ(kUnknownTypeId, raw.item(2).type_id()); + + unittest::TestMessageSetExtension1 message1; + EXPECT_TRUE(message1.ParseFromString(raw.item(0).message())); + EXPECT_EQ(123, message1.i()); + + unittest::TestMessageSetExtension2 message2; + EXPECT_TRUE(message2.ParseFromString(raw.item(1).message())); + EXPECT_EQ("foo", message2.str()); + + EXPECT_EQ("bar", raw.item(2).message()); +} + +TEST(WireFormatTest, ParseMessageSet) { + // Set up a RawMessageSet with two known messages and an unknown one. + unittest::RawMessageSet raw; + + { + unittest::RawMessageSet::Item* item = raw.add_item(); + item->set_type_id( + unittest::TestMessageSetExtension1::descriptor()->extension(0)->number()); + unittest::TestMessageSetExtension1 message; + message.set_i(123); + message.SerializeToString(item->mutable_message()); + } + + { + unittest::RawMessageSet::Item* item = raw.add_item(); + item->set_type_id( + unittest::TestMessageSetExtension2::descriptor()->extension(0)->number()); + unittest::TestMessageSetExtension2 message; + message.set_str("foo"); + message.SerializeToString(item->mutable_message()); + } + + { + unittest::RawMessageSet::Item* item = raw.add_item(); + item->set_type_id(kUnknownTypeId); + item->set_message("bar"); + } + + string data; + ASSERT_TRUE(raw.SerializeToString(&data)); + + // Parse as a TestMessageSet and check the contents. + unittest::TestMessageSet message_set; + ASSERT_TRUE(message_set.ParseFromString(data)); + + EXPECT_EQ(123, message_set.GetExtension( + unittest::TestMessageSetExtension1::message_set_extension).i()); + EXPECT_EQ("foo", message_set.GetExtension( + unittest::TestMessageSetExtension2::message_set_extension).str()); + + ASSERT_EQ(1, message_set.unknown_fields().field_count()); + ASSERT_EQ(1, message_set.unknown_fields().field(0).length_delimited_size()); + EXPECT_EQ("bar", message_set.unknown_fields().field(0).length_delimited(0)); +} + +TEST(WireFormatTest, RecursionLimit) { + unittest::TestRecursiveMessage message; + message.mutable_a()->mutable_a()->mutable_a()->mutable_a()->set_i(1); + string data; + message.SerializeToString(&data); + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(4); + unittest::TestRecursiveMessage message2; + EXPECT_TRUE(message2.ParseFromCodedStream(&input)); + } + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(3); + unittest::TestRecursiveMessage message2; + EXPECT_FALSE(message2.ParseFromCodedStream(&input)); + } +} + +TEST(WireFormatTest, UnknownFieldRecursionLimit) { + unittest::TestEmptyMessage message; + message.mutable_unknown_fields() + ->AddField(1234)->add_group() + ->AddField(1234)->add_group() + ->AddField(1234)->add_group() + ->AddField(1234)->add_group() + ->AddField(1234)->add_varint(123); + string data; + message.SerializeToString(&data); + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(4); + unittest::TestEmptyMessage message2; + EXPECT_TRUE(message2.ParseFromCodedStream(&input)); + } + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(3); + unittest::TestEmptyMessage message2; + EXPECT_FALSE(message2.ParseFromCodedStream(&input)); + } +} + +TEST(WireFormatTest, ZigZag) { +// avoid line-wrapping +#define LL(x) GOOGLE_LONGLONG(x) +#define ULL(x) GOOGLE_ULONGLONG(x) +#define ZigZagEncode32(x) WireFormat::ZigZagEncode32(x) +#define ZigZagDecode32(x) WireFormat::ZigZagDecode32(x) +#define ZigZagEncode64(x) WireFormat::ZigZagEncode64(x) +#define ZigZagDecode64(x) WireFormat::ZigZagDecode64(x) + + EXPECT_EQ(0u, ZigZagEncode32( 0)); + EXPECT_EQ(1u, ZigZagEncode32(-1)); + EXPECT_EQ(2u, ZigZagEncode32( 1)); + EXPECT_EQ(3u, ZigZagEncode32(-2)); + EXPECT_EQ(0x7FFFFFFEu, ZigZagEncode32(0x3FFFFFFF)); + EXPECT_EQ(0x7FFFFFFFu, ZigZagEncode32(0xC0000000)); + EXPECT_EQ(0xFFFFFFFEu, ZigZagEncode32(0x7FFFFFFF)); + EXPECT_EQ(0xFFFFFFFFu, ZigZagEncode32(0x80000000)); + + EXPECT_EQ( 0, ZigZagDecode32(0u)); + EXPECT_EQ(-1, ZigZagDecode32(1u)); + EXPECT_EQ( 1, ZigZagDecode32(2u)); + EXPECT_EQ(-2, ZigZagDecode32(3u)); + EXPECT_EQ(0x3FFFFFFF, ZigZagDecode32(0x7FFFFFFEu)); + EXPECT_EQ(0xC0000000, ZigZagDecode32(0x7FFFFFFFu)); + EXPECT_EQ(0x7FFFFFFF, ZigZagDecode32(0xFFFFFFFEu)); + EXPECT_EQ(0x80000000, ZigZagDecode32(0xFFFFFFFFu)); + + EXPECT_EQ(0u, ZigZagEncode64( 0)); + EXPECT_EQ(1u, ZigZagEncode64(-1)); + EXPECT_EQ(2u, ZigZagEncode64( 1)); + EXPECT_EQ(3u, ZigZagEncode64(-2)); + EXPECT_EQ(ULL(0x000000007FFFFFFE), ZigZagEncode64(LL(0x000000003FFFFFFF))); + EXPECT_EQ(ULL(0x000000007FFFFFFF), ZigZagEncode64(LL(0xFFFFFFFFC0000000))); + EXPECT_EQ(ULL(0x00000000FFFFFFFE), ZigZagEncode64(LL(0x000000007FFFFFFF))); + EXPECT_EQ(ULL(0x00000000FFFFFFFF), ZigZagEncode64(LL(0xFFFFFFFF80000000))); + EXPECT_EQ(ULL(0xFFFFFFFFFFFFFFFE), ZigZagEncode64(LL(0x7FFFFFFFFFFFFFFF))); + EXPECT_EQ(ULL(0xFFFFFFFFFFFFFFFF), ZigZagEncode64(LL(0x8000000000000000))); + + EXPECT_EQ( 0, ZigZagDecode64(0u)); + EXPECT_EQ(-1, ZigZagDecode64(1u)); + EXPECT_EQ( 1, ZigZagDecode64(2u)); + EXPECT_EQ(-2, ZigZagDecode64(3u)); + EXPECT_EQ(LL(0x000000003FFFFFFF), ZigZagDecode64(ULL(0x000000007FFFFFFE))); + EXPECT_EQ(LL(0xFFFFFFFFC0000000), ZigZagDecode64(ULL(0x000000007FFFFFFF))); + EXPECT_EQ(LL(0x000000007FFFFFFF), ZigZagDecode64(ULL(0x00000000FFFFFFFE))); + EXPECT_EQ(LL(0xFFFFFFFF80000000), ZigZagDecode64(ULL(0x00000000FFFFFFFF))); + EXPECT_EQ(LL(0x7FFFFFFFFFFFFFFF), ZigZagDecode64(ULL(0xFFFFFFFFFFFFFFFE))); + EXPECT_EQ(LL(0x8000000000000000), ZigZagDecode64(ULL(0xFFFFFFFFFFFFFFFF))); + + // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1) + // were chosen semi-randomly via keyboard bashing. + EXPECT_EQ( 0, ZigZagDecode32(ZigZagEncode32( 0))); + EXPECT_EQ( 1, ZigZagDecode32(ZigZagEncode32( 1))); + EXPECT_EQ( -1, ZigZagDecode32(ZigZagEncode32( -1))); + EXPECT_EQ(14927, ZigZagDecode32(ZigZagEncode32(14927))); + EXPECT_EQ(-3612, ZigZagDecode32(ZigZagEncode32(-3612))); + + EXPECT_EQ( 0, ZigZagDecode64(ZigZagEncode64( 0))); + EXPECT_EQ( 1, ZigZagDecode64(ZigZagEncode64( 1))); + EXPECT_EQ( -1, ZigZagDecode64(ZigZagEncode64( -1))); + EXPECT_EQ(14927, ZigZagDecode64(ZigZagEncode64(14927))); + EXPECT_EQ(-3612, ZigZagDecode64(ZigZagEncode64(-3612))); + + EXPECT_EQ(LL(856912304801416), ZigZagDecode64(ZigZagEncode64( + LL(856912304801416)))); + EXPECT_EQ(LL(-75123905439571256), ZigZagDecode64(ZigZagEncode64( + LL(-75123905439571256)))); +} + +class WireFormatInvalidInputTest : public testing::Test { + protected: + // Make a serialized TestAllTypes in which the field optional_nested_message + // contains exactly the given bytes, which may be invalid. + string MakeInvalidEmbeddedMessage(const char* bytes, int size) { + const FieldDescriptor* field = + unittest::TestAllTypes::descriptor()->FindFieldByName( + "optional_nested_message"); + GOOGLE_CHECK(field != NULL); + + string result; + + { + io::StringOutputStream raw_output(&result); + io::CodedOutputStream output(&raw_output); + + EXPECT_TRUE(WireFormat::WriteString( + field->number(), string(bytes, size), &output)); + } + + return result; + } + + // Make a serialized TestAllTypes in which the field optionalgroup + // contains exactly the given bytes -- which may be invalid -- and + // possibly no end tag. + string MakeInvalidGroup(const char* bytes, int size, bool include_end_tag) { + const FieldDescriptor* field = + unittest::TestAllTypes::descriptor()->FindFieldByName( + "optionalgroup"); + GOOGLE_CHECK(field != NULL); + + string result; + + { + io::StringOutputStream raw_output(&result); + io::CodedOutputStream output(&raw_output); + + EXPECT_TRUE(output.WriteVarint32(WireFormat::MakeTag(field))); + EXPECT_TRUE(output.WriteString(string(bytes, size))); + if (include_end_tag) { + EXPECT_TRUE( + output.WriteVarint32(WireFormat::MakeTag( + field->number(), WireFormat::WIRETYPE_END_GROUP))); + } + } + + return result; + } +}; + +TEST_F(WireFormatInvalidInputTest, InvalidSubMessage) { + unittest::TestAllTypes message; + + // Control case. + EXPECT_TRUE(message.ParseFromString(MakeInvalidEmbeddedMessage("", 0))); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\0", 1))); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\200", 1))); + + // The byte is an endgroup tag, but we aren't parsing a group. + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\014", 1))); + + // The byte is a valid varint but not a valid tag (bad wire type). + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\017", 1))); +} + +TEST_F(WireFormatInvalidInputTest, InvalidGroup) { + unittest::TestAllTypes message; + + // Control case. + EXPECT_TRUE(message.ParseFromString(MakeInvalidGroup("", 0, true))); + + // Missing end tag. Groups cannot end at EOF. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("", 0, false))); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\0", 1, false))); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\200", 1, false))); + + // The byte is an endgroup tag, but not the right one for this group. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\014", 1, false))); + + // The byte is a valid varint but not a valid tag (bad wire type). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\017", 1, true))); +} + +TEST_F(WireFormatInvalidInputTest, InvalidUnknownGroup) { + // Use ForeignMessage so that the group made by MakeInvalidGroup will not + // be a known tag number. + unittest::ForeignMessage message; + + // Control case. + EXPECT_TRUE(message.ParseFromString(MakeInvalidGroup("", 0, true))); + + // Missing end tag. Groups cannot end at EOF. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("", 0, false))); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\0", 1, false))); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\200", 1, false))); + + // The byte is an endgroup tag, but not the right one for this group. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\014", 1, false))); + + // The byte is a valid varint but not a valid tag (bad wire type). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\017", 1, true))); +} + +} // namespace +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/gtest/CHANGES b/src/gtest/CHANGES new file mode 100644 index 00000000..871ef1fb --- /dev/null +++ b/src/gtest/CHANGES @@ -0,0 +1,3 @@ +Changes for 1.0.0: + + * Initial Open Source release of Google Test diff --git a/src/gtest/CONTRIBUTORS b/src/gtest/CONTRIBUTORS new file mode 100644 index 00000000..31341833 --- /dev/null +++ b/src/gtest/CONTRIBUTORS @@ -0,0 +1,23 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Testing Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Ajay Joshi +Bharat Mediratta +Chandler Carruth +Chris Prince +Chris Taylor +Jeffrey Yasskin +Keir Mierle +Keith Ray +Markus Heule +Patrick Hanna +Patrick Riley +Peter Kaminski +Russ Cox +Russ Rufer +Sean Mcafee +Sigurður Ásgeirsson +Tracy Bialik +Zhanyong Wan diff --git a/src/gtest/COPYING b/src/gtest/COPYING new file mode 100644 index 00000000..1941a11f --- /dev/null +++ b/src/gtest/COPYING @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google 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. diff --git a/src/gtest/README b/src/gtest/README new file mode 100644 index 00000000..0e170475 --- /dev/null +++ b/src/gtest/README @@ -0,0 +1,150 @@ +This directory contains Google Test, described below. It is used by the +Protocol Buffer C++ unit tests. If you would like to use Google Test +yourself, you should probably download it from the URL mentioned below, +not attempt to use the sources in this package. + +Two changes were made from the original sources: +* gtest.cc's #include of gtest-internal-inl.h was modified to reflect the + environment it is being built in (replaced "src/" with "gtest/"). +* GetThreadCount() in gtest-port.h was hard-coded to return 1 rather than 0, + since the Protocol Buffer tests do not use threads. + +The original Google Test README follows. + +====================================================================== + +Google C++ Testing Framework +============================ +http://code.google.com/p/googletest/ + +Overview +-------- +Google's framework for writing C++ tests on a variety of platforms (Linux, Mac +OS X, Windows, Windows CE, and Symbian). Based on the xUnit architecture. +Supports automatic test discovery, a rich set of assertions, user-defined +assertions, death tests, fatal and non-fatal failures, various options for +running the tests, and XML test report generation. + +Please see the project page above for more information as well as mailing lists +for questions, discussions, and development. There is also an IRC channel on +OFTC (irc.oftc.net) #gtest available. Please join us! + +Requirements +------------ +Google Test is designed to have fairly minimal requirements to build and use +with your projects, but there are some. Currently, the only Operating System +(OS) on which Google Test is known to build properly is Linux, but we are +actively working on Windows and Mac support as well. The source code itself is +already portable across many other platforms, but we are still developing +robust build systems for each. + +### Linux Requirements ### +These are the base requirements to build and use Google Test from a source +package (as described below): + * GNU-compatible Make or "gmake" + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * A C++98 standards compliant compiler + +Furthermore, if you are building Google Test from a VCS Checkout (also +described below), there are further requirements: + * Automake version 1.9 or newer + * Autoconf version 2.59 or newer + * Libtool / Libtoolize + * Python version 2.4 or newer + +Getting the Source +------------------ +There are two primary ways of getting Google Test's source code: you can +download a source release in your preferred archive format, or directly check +out the source from a Version Control System (VCS, we use Google Code's +Subversion hosting). The VCS checkout requires a few extra steps and some extra +software packages on your system, but lets you track development, and make +patches to contribute much more easily, so we highly encourage it. + +### VCS Checkout: ### +The first step is to select whether you want to check out the main line of +development on Google Test, or one of the released branches. The former will be +much more active and have the latest features, but the latter provides much +more stability and predictability. Choose whichever fits your needs best, and +proceed with the following Subversion commands: + + $ svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn + +or for a release version X.Y.*'s branch: + + $ svn checkout http://googletest.googlecode.com/svn/branches/release-X.Y/ gtest-X.Y-svn + +Next you will need to prepare the GNU Autotools build system. Enter the +target directory of the checkout command you used ('gtest-svn' or +'gtest-X.Y-svn' above) and proceed with the following commands: + + $ aclocal-1.9 # Where "1.9" must match the following automake command + $ libtoolize -c + $ autoheader + $ automake-1.9 -ac # See Automake version requirements above + $ autoconf + +While this is a bit complicated, it will most often be automatically re-run by +your "make" invocations, so in practice you shouldn't need to worry too much. +Once you have completed these steps, your VCS checkout should be equivalent to +a source package, and you may continue with those directions, skipping over the +acquiring and unpacking of the source itself, as the VCS has done that for you. + +### Source Package: ### +Google Test is also released in source packages which can be downloaded from +its Google Code download page[1]. Several different archive formats are +provided, but the only difference is the tools used to manipulate them, and the +size of the resulting file. Download whichever you are most comfortable with. + + [1] Google Test Downloads: http://code.google.com/p/googletest/downloads/list + +Once downloaded expand the archive using whichever tools you prefer for that +type. This will always result in a new directory with the name "gtest-X.Y.Z" +which contains all of the source code. Here are some examples in Linux: + + $ tar -xvzf gtest-X.Y.Z.tar.gz + $ tar -xvjf gtest-X.Y.Z.tar.bz2 + $ unzip gtest-X.Y.Z.zip + +Building the Source +------------------- +There are two primary options for building the source at this point: build it +inside the source code tree, or in a separate directory. We recommend building +in a separate directory as that tends to produce both more consistent results +and be easier to clean up should anything go wrong, but both patterns are +supported. The only hard restriction is that while the build directory can be +a subdirectory of the source directory, the opposite is not possible and will +result in errors. Once you have selected where you wish to build Google Test, +create the directory if necessary, and enter it. The following steps apply for +either approach by simply substituting the shell variable SRCDIR with "." for +building inside the source directory, and the relative path to the source +directory otherwise. + + $ ${SRCDIR}/configure # Standard GNU configure script, --help for more info + $ make # Standard makefile following GNU conventions + +Other programs will only be able to use Google Test's functionality if you +install it in a location which they can access, in Linux this is typically +under '/usr/local'. The following command will install all of the Google Test +libraries, public headers, and utilities necessary for other programs and +libraries to leverage it: + + $ sudo make install # Not necessary, but allows use by other programs + +TODO(chandlerc@google.com): This section needs to be expanded when the +'gtest-config' script is finished and Autoconf macro's are provided (or not +provided) in order to properly reflect the process for other programs to +locate, include, and link against Google Test. + +Finally, should you need to remove Google Test from your system after having +installed it, run the following command, and it will back out its changes. +However, note carefully that you must run this command on the *same* Google +Test build that you ran the install from, or the results are not predictable. +If you install Google Test on your system, and are working from a VCS checkout, +make sure you run this *before* updating your checkout of the source in order +to uninstall the same version which you installed. + + $ sudo make uninstall # Must be run against the exact same build as "install" + +Happy testing! diff --git a/src/gtest/gen_gtest_pred_impl.py b/src/gtest/gen_gtest_pred_impl.py new file mode 100755 index 00000000..7cb31da2 --- /dev/null +++ b/src/gtest/gen_gtest_pred_impl.py @@ -0,0 +1,733 @@ +#!/usr/bin/python2.4 +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google 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. + +"""gen_gtest_pred_impl.py v0.1 + +Generates the implementation of Google Test predicate assertions and +accompanying tests. + +Usage: + + gen_gtest_pred_impl.py MAX_ARITY + +where MAX_ARITY is a positive integer. + +The command generates the implementation of up-to MAX_ARITY-ary +predicate assertions, and writes it to file gtest_pred_impl.h in the +directory where the script is. It also generates the accompanying +unit test in file gtest_pred_impl_unittest.cc. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import sys +import time + +# Where this script is. +SCRIPT_DIR = os.path.dirname(sys.argv[0]) + +# Where to store the generated header. +HEADER = os.path.join(SCRIPT_DIR, '../include/gtest/gtest_pred_impl.h') + +# Where to store the generated unit test. +UNIT_TEST = os.path.join(SCRIPT_DIR, '../test/gtest_pred_impl_unittest.cc') + + +def HeaderPreamble(n): + """Returns the preamble for the header file. + + Args: + n: the maximum arity of the predicate macros to be generated. + """ + + # A map that defines the values used in the preamble template. + DEFS = { + 'today' : time.strftime('%m/%d/%Y'), + 'year' : time.strftime('%Y'), + 'command' : '%s %s' % (os.path.basename(sys.argv[0]), n), + 'n' : n + } + + return ( +"""// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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 is AUTOMATICALLY GENERATED on %(today)s by command +// '%(command)s'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most %(n)s. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT(expression, on_failure) \\ + GTEST_AMBIGUOUS_ELSE_BLOCKER \\ + if (const ::testing::AssertionResult gtest_ar = (expression)) \\ + ; \\ + else \\ + on_failure(gtest_ar.failure_message()) +""" % DEFS) + + +def Arity(n): + """Returns the English name of the given arity.""" + + if n < 0: + return None + elif n <= 3: + return ['nullary', 'unary', 'binary', 'ternary'][n] + else: + return '%s-ary' % n + + +def Title(word): + """Returns the given word in title case. The difference between + this and string's title() method is that Title('4-ary') is '4-ary' + while '4-ary'.title() is '4-Ary'.""" + + return word[0].upper() + word[1:] + + +def OneTo(n): + """Returns the list [1, 2, 3, ..., n].""" + + return range(1, n + 1) + + +def Iter(n, format, sep=''): + """Given a positive integer n, a format string that contains 0 or + more '%s' format specs, and optionally a separator string, returns + the join of n strings, each formatted with the format string on an + iterator ranged from 1 to n. + + Example: + + Iter(3, 'v%s', sep=', ') returns 'v1, v2, v3'. + """ + + # How many '%s' specs are in format? + spec_count = len(format.split('%s')) - 1 + return sep.join([format % (spec_count * (i,)) for i in OneTo(n)]) + + +def ImplementationForArity(n): + """Returns the implementation of n-ary predicate assertions.""" + + # A map the defines the values used in the implementation template. + DEFS = { + 'n' : str(n), + 'vs' : Iter(n, 'v%s', sep=', '), + 'vts' : Iter(n, '#v%s', sep=', '), + 'arity' : Arity(n), + 'Arity' : Title(Arity(n)) + } + + impl = """ + +// Helper function for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use +// this in your code. +template +AssertionResult AssertPred%(n)sHelper(const char* pred_text""" % DEFS + + impl += Iter(n, """, + const char* e%s""") + + impl += """, + Pred pred""" + + impl += Iter(n, """, + const T%s& v%s""") + + impl += """) { + if (pred(%(vs)s)) return AssertionSuccess(); + + Message msg; +""" % DEFS + + impl += ' msg << pred_text << "("' + + impl += Iter(n, """ + << e%s""", sep=' << ", "') + + impl += ' << ") evaluates to false, where"' + + impl += Iter(n, """ + << "\\n" << e%s << " evaluates to " << v%s""") + + impl += """; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT%(n)s. +// Don't use this in your code. +#define GTEST_PRED_FORMAT%(n)s(pred_format, %(vs)s, on_failure)\\ + GTEST_ASSERT(pred_format(%(vts)s, %(vs)s),\\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use +// this in your code. +#define GTEST_PRED%(n)s(pred, %(vs)s, on_failure)\\ + GTEST_ASSERT(::testing::AssertPred%(n)sHelper(#pred""" % DEFS + + impl += Iter(n, """, \\ + #v%s""") + + impl += """, \\ + pred""" + + impl += Iter(n, """, \\ + v%s""") + + impl += """), on_failure) + +// %(Arity)s predicate assertion macros. +#define EXPECT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\ + GTEST_PRED_FORMAT%(n)s(pred_format, %(vs)s, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED%(n)s(pred, %(vs)s) \\ + GTEST_PRED%(n)s(pred, %(vs)s, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\ + GTEST_PRED_FORMAT%(n)s(pred_format, %(vs)s, GTEST_FATAL_FAILURE) +#define ASSERT_PRED%(n)s(pred, %(vs)s) \\ + GTEST_PRED%(n)s(pred, %(vs)s, GTEST_FATAL_FAILURE) + +""" % DEFS + + return impl + + +def HeaderPostamble(): + """Returns the postamble for the header file.""" + + return """ + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +""" + + +def GenerateFile(path, content): + """Given a file path and a content string, overwrites it with the + given content.""" + + print 'Updating file %s . . .' % path + + f = file(path, 'w+') + print >>f, content, + f.close() + + print 'File %s has been updated.' % path + + +def GenerateHeader(n): + """Given the maximum arity n, updates the header file that implements + the predicate assertions.""" + + GenerateFile(HEADER, + HeaderPreamble(n) + + ''.join([ImplementationForArity(i) for i in OneTo(n)]) + + HeaderPostamble()) + + +def UnitTestPreamble(): + """Returns the preamble for the unit test file.""" + + # A map that defines the values used in the preamble template. + DEFS = { + 'today' : time.strftime('%m/%d/%Y'), + 'year' : time.strftime('%Y'), + 'command' : '%s %s' % (os.path.basename(sys.argv[0]), sys.argv[1]), + } + + return ( +"""// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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 is AUTOMATICALLY GENERATED on %(today)s by command +// '%(command)s'. DO NOT EDIT BY HAND! + +// Regression test for gtest_pred_impl.h +// +// This file is generated by a script and quite long. If you intend to +// learn how Google Test works by reading its unit tests, read +// gtest_unittest.cc instead. +// +// This is intended as a regression test for the Google Test predicate +// assertions. We compile it as part of the gtest_unittest target +// only to keep the implementation tidy and compact, as it is quite +// involved to set up the stage for testing Google Test using Google +// Test itself. +// +// Currently, gtest_unittest takes ~11 seconds to run in the testing +// daemon. In the future, if it grows too large and needs much more +// time to finish, we should consider separating this file into a +// stand-alone regression test. + +#include + +#include +#include + +// A user-defined data type. +struct Bool { + explicit Bool(int val) : value(val != 0) {} + + bool operator>(int n) const { return value > Bool(n).value; } + + Bool operator+(const Bool& rhs) const { return Bool(value + rhs.value); } + + bool operator==(const Bool& rhs) const { return value == rhs.value; } + + bool value; +}; + +// Enables Bool to be used in assertions. +std::ostream& operator<<(std::ostream& os, const Bool& x) { + return os << (x.value ? "true" : "false"); +} + +""" % DEFS) + + +def TestsForArity(n): + """Returns the tests for n-ary predicate assertions.""" + + # A map that defines the values used in the template for the tests. + DEFS = { + 'n' : n, + 'es' : Iter(n, 'e%s', sep=', '), + 'vs' : Iter(n, 'v%s', sep=', '), + 'vts' : Iter(n, '#v%s', sep=', '), + 'tvs' : Iter(n, 'T%s v%s', sep=', '), + 'int_vs' : Iter(n, 'int v%s', sep=', '), + 'Bool_vs' : Iter(n, 'Bool v%s', sep=', '), + 'types' : Iter(n, 'typename T%s', sep=', '), + 'v_sum' : Iter(n, 'v%s', sep=' + '), + 'arity' : Arity(n), + 'Arity' : Title(Arity(n)), + } + + tests = ( +"""// Sample functions/functors for testing %(arity)s predicate assertions. + +// A %(arity)s predicate function. +template <%(types)s> +bool PredFunction%(n)s(%(tvs)s) { + return %(v_sum)s > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction%(n)sInt(%(int_vs)s) { + return %(v_sum)s > 0; +} +bool PredFunction%(n)sBool(%(Bool_vs)s) { + return %(v_sum)s > 0; +} +""" % DEFS) + + tests += """ +// A %(arity)s predicate functor. +struct PredFunctor%(n)s { + template <%(types)s> + bool operator()(""" % DEFS + + tests += Iter(n, 'const T%s& v%s', sep=""", + """) + + tests += """) { + return %(v_sum)s > 0; + } +}; +""" % DEFS + + tests += """ +// A %(arity)s predicate-formatter function. +template <%(types)s> +testing::AssertionResult PredFormatFunction%(n)s(""" % DEFS + + tests += Iter(n, 'const char* e%s', sep=""", + """) + + tests += Iter(n, """, + const T%s& v%s""") + + tests += """) { + if (PredFunction%(n)s(%(vs)s)) + return testing::AssertionSuccess(); + + testing::Message msg; + msg << """ % DEFS + + tests += Iter(n, 'e%s', sep=' << " + " << ') + + tests += """ + << " is expected to be positive, but evaluates to " + << %(v_sum)s << "."; + return testing::AssertionFailure(msg); +} +""" % DEFS + + tests += """ +// A %(arity)s predicate-formatter functor. +struct PredFormatFunctor%(n)s { + template <%(types)s> + testing::AssertionResult operator()(""" % DEFS + + tests += Iter(n, 'const char* e%s', sep=""", + """) + + tests += Iter(n, """, + const T%s& v%s""") + + tests += """) const { + return PredFormatFunction%(n)s(%(es)s, %(vs)s); + } +}; +""" % DEFS + + tests += """ +// Tests for {EXPECT|ASSERT}_PRED_FORMAT%(n)s. + +class Predicate%(n)sTest : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false;""" % DEFS + + tests += """ + """ + Iter(n, 'n%s_ = ') + """0; + } +""" + + tests += """ + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once.""" + + tests += ''.join([""" + EXPECT_EQ(1, n%s_) << + "The predicate assertion didn't evaluate argument %s " + "exactly once.";""" % (i, i + 1) for i in OneTo(n)]) + + tests += """ + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; +""" % DEFS + + tests += Iter(n, """ + static int n%s_;""") + + tests += """ +}; + +bool Predicate%(n)sTest::expected_to_finish_; +bool Predicate%(n)sTest::finished_; +""" % DEFS + + tests += Iter(n, """int Predicate%%(n)sTest::n%s_; +""") % DEFS + + tests += """ +typedef Predicate%(n)sTest EXPECT_PRED_FORMAT%(n)sTest; +typedef Predicate%(n)sTest ASSERT_PRED_FORMAT%(n)sTest; +typedef Predicate%(n)sTest EXPECT_PRED%(n)sTest; +typedef Predicate%(n)sTest ASSERT_PRED%(n)sTest; +""" % DEFS + + def GenTest(use_format, use_assert, expect_failure, + use_functor, use_user_type): + """Returns the test for a predicate assertion macro. + + Args: + use_format: true iff the assertion is a *_PRED_FORMAT*. + use_assert: true iff the assertion is a ASSERT_*. + expect_failure: true iff the assertion is expected to fail. + use_functor: true iff the first argument of the assertion is + a functor (as opposed to a function) + use_user_type: true iff the predicate functor/function takes + argument(s) of a user-defined type. + + Example: + + GenTest(1, 0, 0, 1, 0) returns a test that tests the behavior + of a successful EXPECT_PRED_FORMATn() that takes a functor + whose arguments have built-in types.""" + + if use_assert: + assrt = 'ASSERT' # 'assert' is reserved, so we cannot use + # that identifier here. + else: + assrt = 'EXPECT' + + assertion = assrt + '_PRED' + + if use_format: + pred_format = 'PredFormat' + assertion += '_FORMAT' + else: + pred_format = 'Pred' + + assertion += '%(n)s' % DEFS + + if use_functor: + pred_format_type = 'functor' + pred_format += 'Functor%(n)s()' + else: + pred_format_type = 'function' + pred_format += 'Function%(n)s' + if not use_format: + if use_user_type: + pred_format += 'Bool' + else: + pred_format += 'Int' + + test_name = pred_format_type.title() + + if use_user_type: + arg_type = 'user-defined type (Bool)' + test_name += 'OnUserType' + if expect_failure: + arg = 'Bool(n%s_++)' + else: + arg = 'Bool(++n%s_)' + else: + arg_type = 'built-in type (int)' + test_name += 'OnBuiltInType' + if expect_failure: + arg = 'n%s_++' + else: + arg = '++n%s_' + + if expect_failure: + successful_or_failed = 'failed' + expected_or_not = 'expected.' + test_name += 'Failure' + else: + successful_or_failed = 'successful' + expected_or_not = 'UNEXPECTED!' + test_name += 'Success' + + # A map that defines the values used in the test template. + defs = DEFS.copy() + defs.update({ + 'assert' : assrt, + 'assertion' : assertion, + 'test_name' : test_name, + 'pf_type' : pred_format_type, + 'pf' : pred_format, + 'arg_type' : arg_type, + 'arg' : arg, + 'successful' : successful_or_failed, + 'expected' : expected_or_not, + }) + + test = """ +// Tests a %(successful)s %(assertion)s where the +// predicate-formatter is a %(pf_type)s on a %(arg_type)s. +TEST_F(%(assertion)sTest, %(test_name)s) {""" % defs + + indent = (len(assertion) + 3)*' ' + extra_indent = '' + + if expect_failure: + extra_indent = ' ' + if use_assert: + test += """ + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT""" + else: + test += """ + EXPECT_NONFATAL_FAILURE({ // NOLINT""" + + test += '\n' + extra_indent + """ %(assertion)s(%(pf)s""" % defs + + test = test % defs + test += Iter(n, ',\n' + indent + extra_indent + '%(arg)s' % defs) + test += ');\n' + extra_indent + ' finished_ = true;\n' + + if expect_failure: + test += ' }, "");\n' + + test += '}\n' + return test + + # Generates tests for all 2**6 = 64 combinations. + tests += ''.join([GenTest(use_format, use_assert, expect_failure, + use_functor, use_user_type) + for use_format in [0, 1] + for use_assert in [0, 1] + for expect_failure in [0, 1] + for use_functor in [0, 1] + for use_user_type in [0, 1] + ]) + + return tests + + +def UnitTestPostamble(): + """Returns the postamble for the tests.""" + + return '' + + +def GenerateUnitTest(n): + """Returns the tests for up-to n-ary predicate assertions.""" + + GenerateFile(UNIT_TEST, + UnitTestPreamble() + + ''.join([TestsForArity(i) for i in OneTo(n)]) + + UnitTestPostamble()) + + +def _Main(): + """The entry point of the script. Generates the header file and its + unit test.""" + + if len(sys.argv) != 2: + print __doc__ + print 'Author: ' + __author__ + sys.exit(1) + + n = int(sys.argv[1]) + GenerateHeader(n) + GenerateUnitTest(n) + + +if __name__ == '__main__': + _Main() diff --git a/src/gtest/gtest-death-test.cc b/src/gtest/gtest-death-test.cc new file mode 100644 index 00000000..09fdd3ff --- /dev/null +++ b/src/gtest/gtest-death-test.cc @@ -0,0 +1,751 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) +// +// This file implements death tests. + +#include +#include + +#include +#include +#include + +#include +#include + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION +#include "gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +namespace internal { +GTEST_DEFINE_string( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "colons. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#ifdef GTEST_HAS_DEATH_TEST + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; +} + +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static String ExitSummary(int exit_code) { + Message m; + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +#ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +#endif + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static String DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} + +// Static string containing a description of the outcome of the +// last death test. +static String last_death_test_message; + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test +// can conclude. DIED means that the process died while executing the +// test code; LIVED means that process lived beyond the end of the test +// code; and RETURNED means that the test statement attempted a "return," +// which is not allowed. IN_PROGRESS means the test has not yet +// concluded. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const char* format, ...) { + // This function may be called from a threadsafe-style death test + // child process, which operates on a very small stack. Use the + // heap for any additional non-miniscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + va_list args; + va_start(args, format); + + if (flag != NULL) { + FILE* parent = fdopen(flag->status_fd, "w"); + fputc(kDeathTestInternalError, parent); + vfprintf(parent, format, args); + fclose(parent); + va_end(args); + _exit(1); + } else { + vfprintf(stderr, format, args); + va_end(args); + abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +#define GTEST_DEATH_TEST_CHECK(expression) \ + do { \ + if (!(expression)) { \ + DeathTestAbort("CHECK failed: File %s, line %d: %s", \ + __FILE__, __LINE__, #expression); \ + } \ + } while (0) + +// This macro is similar to GTEST_DEATH_TEST_CHECK, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +#define GTEST_DEATH_TEST_CHECK_SYSCALL(expression) \ + do { \ + int retval; \ + do { \ + retval = (expression); \ + } while (retval == -1 && errno == EINTR); \ + if (retval == -1) { \ + DeathTestAbort("CHECK failed: File %s, line %d: %s != -1", \ + __FILE__, __LINE__, #expression); \ + } \ + } while (0) + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message.c_str(); +} + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTest { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual bool Passed(bool status_ok); + virtual void Abort(AbortReason reason); + + protected: + void set_forked(bool forked) { forked_ = forked; } + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + void set_read_fd(int fd) { read_fd_ = fd; } + void set_write_fd(int fd) { write_fd_ = fd; } + + private: + // The textual content of the code this object is testing. + const char* const statement_; + // The regular expression which test output must match. + const RE* const regex_; + // True if the death test successfully forked. + bool forked_; + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; + // File descriptors for communicating the death test's status byte. + int read_fd_; // Always -1 in the child process. + int write_fd_; // Always -1 in the parent process. + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* statement, const RE* regex) + : DeathTest(), + statement_(statement), + regex_(regex), + forked_(false), + child_pid_(-1), + read_fd_(-1), + write_fd_(-1), + status_(-1), + outcome_(IN_PROGRESS) { +} + +// Reads an internal failure message from a file descriptor, then calls +// LOG(FATAL) with that message. Called from a death test parent process +// to read a failure message from the death test child process. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + ssize_t num_read; + + do { + while ((num_read = read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + // TODO(smcafee): Maybe just FAIL the test instead? + if (num_read == 0) { + GTEST_LOG(FATAL, error); + } else { + GTEST_LOG(FATAL, + Message() << "Error while reading death test internal: " + << strerror(errno) << " [" << errno << "]"); + } +} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!forked_) + return 0; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + char flag; + ssize_t bytes_read; + + do { + bytes_read = read(read_fd_, &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + outcome_ = DIED; + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + outcome_ = RETURNED; + break; + case kDeathTestLived: + outcome_ = LIVED; + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd_); // Does not return. + break; + default: + GTEST_LOG(FATAL, + Message() << "Death test child process reported unexpected " + << "status byte (" << static_cast(flag) + << ")"); + } + } else { + GTEST_LOG(FATAL, + Message() << "Read from death test child process failed: " + << strerror(errno)); + } + + GTEST_DEATH_TEST_CHECK_SYSCALL(close(read_fd_)); + GTEST_DEATH_TEST_CHECK_SYSCALL(waitpid(child_pid_, &status_, 0)); + return status_; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: an enumeration describing how the death test +// concluded: DIED, LIVED, or RETURNED. The death test fails +// in the latter two cases +// status: the exit status of the child process, in the format +// specified by wait(2) +// regex: a regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the static variable last_death_test_message. +bool ForkingDeathTest::Passed(bool status_ok) { + if (!forked_) + return false; + +#if GTEST_HAS_GLOBAL_STRING + const ::string error_message = GetCapturedStderr(); +#else + const ::std::string error_message = GetCapturedStderr(); +#endif // GTEST_HAS_GLOBAL_STRING + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement_ << "\n"; + switch (outcome_) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg: " << error_message; + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg: " << error_message; + break; + case DIED: + if (status_ok) { + if (RE::PartialMatch(error_message, *regex_)) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex_->pattern() << "\n" + << "Actual msg: " << error_message; + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status_) << "\n"; + } + break; + default: + GTEST_LOG(FATAL, + "DeathTest::Passed somehow called before conclusion of test"); + } + + last_death_test_message = buffer.GetString(); + return success; +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file desriptor, then +// calls _exit(1). +void ForkingDeathTest::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char flag = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : kDeathTestReturned; + GTEST_DEATH_TEST_CHECK_SYSCALL(write(write_fd_, &flag, 1)); + GTEST_DEATH_TEST_CHECK_SYSCALL(close(write_fd_)); + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* statement, const RE* regex) : + ForkingDeathTest(statement, regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG(WARNING, DeathTestThreadWarning(thread_count)); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK(pipe(pipe_fd) != -1); + + last_death_test_message = ""; + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_forked(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* statement, const RE* regex, + const char* file, int line) : + ForkingDeathTest(statement, regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + ~Arguments() { + for (std::vector::iterator i = args_.begin(); + i + 1 != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, strdup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, strdup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +// The main function for a threadsafe-style death test child process. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL(close(args->close_fd)); + execve(args->argv[0], args->argv, environ); + DeathTestAbort("execve failed: %s", strerror(errno)); + return EXIT_FAILURE; +} + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +static bool StackLowerThanAddress(const void* ptr) { + int dummy; + return &dummy < ptr; +} + +static bool StackGrowsDown() { + int dummy; + return StackLowerThanAddress(&dummy); +} + +// A threadsafe implementation of fork(2) for threadsafe-style death tests +// that uses clone(2). It dies with an error message if anything goes +// wrong. +static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK(stack != MAP_FAILED); + void* const stack_top = + static_cast(stack) + (stack_grows_down ? stack_size : 0); + ExecDeathTestArgs args = { argv, close_fd }; + const pid_t child_pid = clone(&ExecDeathTestChildMain, stack_top, + SIGCHLD, &args); + GTEST_DEATH_TEST_CHECK(child_pid != -1); + GTEST_DEATH_TEST_CHECK(munmap(stack, stack_size) != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->status_fd); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const String filter_flag = + String::Format("--%s%s=%s.%s", + GTEST_FLAG_PREFIX, kFilterFlag, + info->test_case_name(), info->name()); + const String internal_flag = + String::Format("--%s%s=%s:%d:%d:%d", + GTEST_FLAG_PREFIX, kInternalRunDeathTestFlag, file_, line_, + death_test_index, pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvs()); + args.AddArgument("--logtostderr"); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + last_death_test_message = ""; + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_forked(true); + return OVERSEE_TEST; +} + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index) { + last_death_test_message = String::Format( + "Death test count (%d) somehow exceeded expected maximum (%d)", + death_test_index, flag->index); + return false; + } + + if (!(flag->file == file && flag->line == line && + flag->index == death_test_index)) { + *test = NULL; + return true; + } + } + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } else { + last_death_test_message = String::Format( + "Unknown death test style \"%s\" encountered", + GTEST_FLAG(death_test_style).c_str()); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (true) { + const ::std::string::size_type colon = str.find(':', pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +// Attempts to parse a string into a positive integer. Returns true +// if that is possible. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static bool ParsePositiveInt(const ::std::string& str, int* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtol's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !isdigit(str[0])) { + return false; + } + char* endptr; + const long parsed = strtol(str.c_str(), &endptr, 10); // NOLINT + if (*endptr == '\0' && parsed <= INT_MAX) { + *number = static_cast(parsed); + return true; + } else { + return false; + } +} + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + InternalRunDeathTestFlag* const internal_run_death_test_flag = + new InternalRunDeathTestFlag; + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), ':', &fields); + if (fields.size() != 4 + || !ParsePositiveInt(fields[1], &internal_run_death_test_flag->line) + || !ParsePositiveInt(fields[2], &internal_run_death_test_flag->index) + || !ParsePositiveInt(fields[3], + &internal_run_death_test_flag->status_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: %s", + GTEST_FLAG(internal_run_death_test).c_str()); + } + internal_run_death_test_flag->file = fields[0].c_str(); + return internal_run_death_test_flag; +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing diff --git a/src/gtest/gtest-death-test.h b/src/gtest/gtest-death-test.h new file mode 100644 index 00000000..cbd41fe6 --- /dev/null +++ b/src/gtest/gtest-death-test.h @@ -0,0 +1,205 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string(death_test_style); + +#ifdef GTEST_HAS_DEATH_TEST + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. The assertion fails immediately if there are more than one +// active threads. This is because it's safe to fork() only when +// there is a single thread. +// +// 2. The parent process forks a sub-process and runs the death test +// in it; the sub-process exits with code 0 at the end of the death +// test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Note: +// +// It's not safe to call exit() if the current process is forked from +// a multi-threaded process, so people usually call _exit() instead in +// such a case. However, we are not concerned with this as we run +// death tests only when there is a single thread. Since exit() has a +// cleaner semantics (it also calls functions registered with atexit() +// and on_exit()), this macro calls exit() instead of _exit() to +// terminate the child process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i); +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +#define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST(statement, predicate, regex, GTEST_FATAL_FAILURE) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +#define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST(statement, predicate, regex, GTEST_NONFATAL_FAILURE) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +#define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +#define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + bool operator()(int exit_status) const; + private: + const int exit_code_; +}; + +// Tests that an exit code describes an exit due to termination by a +// given signal. +class KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +#ifdef NDEBUG + +#define EXPECT_DEBUG_DEATH(statement, regex) \ + do { statement; } while (false) + +#define ASSERT_DEBUG_DEATH(statement, regex) \ + do { statement; } while (false) + +#else + +#define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +#endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/src/gtest/gtest-filepath.cc b/src/gtest/gtest-filepath.cc new file mode 100644 index 00000000..2fba96ea --- /dev/null +++ b/src/gtest/gtest-filepath.cc @@ -0,0 +1,208 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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. +// +// Authors: keith.ray@gmail.com (Keith Ray) + +#include +#include + +#ifdef _WIN32 +#include +#include +#endif // _WIN32 + +#include + +#include + +namespace testing { +namespace internal { + +#ifdef GTEST_OS_WINDOWS +const char kPathSeparator = '\\'; +const char kPathSeparatorString[] = "\\"; +const char kCurrentDirectoryString[] = ".\\"; +#else +const char kPathSeparator = '/'; +const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + String dot_extension(String::Format(".%s", extension)); + if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) { + return FilePath(String(pathname_.c_str(), pathname_.GetLength() - 4)); + } + return *this; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); + return last_sep ? FilePath(String(last_sep + 1)) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); + return FilePath(last_sep ? String(c_str(), last_sep + 1 - c_str()) + : String(kCurrentDirectoryString)); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + FilePath dir(directory.RemoveTrailingPathSeparator()); + if (number == 0) { + return FilePath(String::Format("%s%c%s.%s", dir.c_str(), kPathSeparator, + base_name.c_str(), extension)); + } + return FilePath(String::Format("%s%c%s_%d.%s", dir.c_str(), kPathSeparator, + base_name.c_str(), number, extension)); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#ifdef GTEST_OS_WINDOWS + struct _stat file_stat = {}; + return _stat(pathname_.c_str(), &file_stat) == 0; +#else + struct stat file_stat = {}; + return stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#ifdef _WIN32 + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + struct _stat file_stat = {}; + result = _stat(removed_sep.c_str(), &file_stat) == 0 && + (_S_IFDIR & file_stat.st_mode) != 0; +#else + struct stat file_stat = {}; + result = stat(pathname_.c_str(), &file_stat) == 0 && + S_ISDIR(file_stat.st_mode); +#endif // _WIN32 + return result; +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return pathname_.EndsWith(kPathSeparatorString); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.GetLength() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#ifdef _WIN32 + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // _WIN32 + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return pathname_.EndsWith(kPathSeparatorString) + ? FilePath(String(pathname_.c_str(), pathname_.GetLength() - 1)) + : *this; +} + +} // namespace internal +} // namespace testing diff --git a/src/gtest/gtest-internal-inl.h b/src/gtest/gtest-internal-inl.h new file mode 100644 index 00000000..2a7d71cb --- /dev/null +++ b/src/gtest/gtest-internal-inl.h @@ -0,0 +1,1118 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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 and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION is defined iff the current translation unit is +// part of Google Test's implementation. +#ifndef GTEST_IMPLEMENTATION +// A user is trying to include this from his code - just say no. +#error "gtest-internal-inl.h is part of Google Test's internal implementation." +#error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION + +#include + +#include + +#ifdef GTEST_OS_WINDOWS +#include // NOLINT +#endif // GTEST_OS_WINDOWS + +#include // NOLINT +#include +#include + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify these flags in the code, but want +// Google Test's own unit tests to be able to access them. Therefore we +// declare them here as opposed to in gtest.h. +GTEST_DECLARE_bool(break_on_failure); +GTEST_DECLARE_bool(catch_exceptions); +GTEST_DECLARE_string(color); +GTEST_DECLARE_string(filter); +GTEST_DECLARE_bool(list_tests); +GTEST_DECLARE_string(output); +GTEST_DECLARE_int32(repeat); +GTEST_DECLARE_int32(stack_trace_depth); +GTEST_DECLARE_bool(show_internal_stack_frames); + +namespace internal { + +// Names of the flags (needed for parsing Google Test flags). +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kColorFlag[] = "color"; +const char kRepeatFlag[] = "repeat"; + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + repeat_ = GTEST_FLAG(repeat); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(repeat) = repeat_; + } + private: + // Fields for saving the original values of flags. + bool break_on_failure_; + bool catch_exceptions_; + String color_; + String death_test_style_; + String filter_; + String internal_run_death_test_; + bool list_tests_; + String output_; + bool pretty_; + internal::Int32 repeat_; +} GTEST_ATTRIBUTE_UNUSED; + +// Converts a Unicode code-point to its UTF-8 encoding. +String ToUtf8String(wchar_t wchar); + +// Returns the number of active threads, or 0 when there is an error. +size_t GetThreadCount(); + +// List is a simple singly-linked list container. +// +// We cannot use std::list as Microsoft's implementation of STL has +// problems when exception is disabled. There is a hack to work +// around this, but we've seen cases where the hack fails to work. +// +// TODO(wan): switch to std::list when we have a reliable fix for the +// STL problem, e.g. when we upgrade to the next version of Visual +// C++, or (more likely) switch to STLport. +// +// The element type must support copy constructor. + +// Forward declare List +template // E is the element type. +class List; + +// ListNode is a node in a singly-linked list. It consists of an +// element and a pointer to the next node. The last node in the list +// has a NULL value for its next pointer. +template // E is the element type. +class ListNode { + friend class List; + + private: + + E element_; + ListNode * next_; + + // The c'tor is private s.t. only in the ListNode class and in its + // friend class List we can create a ListNode object. + // + // Creates a node with a given element value. The next pointer is + // set to NULL. + // + // ListNode does NOT have a default constructor. Always use this + // constructor (with parameter) to create a ListNode object. + explicit ListNode(const E & element) : element_(element), next_(NULL) {} + + // We disallow copying ListNode + GTEST_DISALLOW_COPY_AND_ASSIGN(ListNode); + + public: + + // Gets the element in this node. + E & element() { return element_; } + const E & element() const { return element_; } + + // Gets the next node in the list. + ListNode * next() { return next_; } + const ListNode * next() const { return next_; } +}; + + +// List is a simple singly-linked list container. +template // E is the element type. +class List { + public: + + // Creates an empty list. + List() : head_(NULL), last_(NULL), size_(0) {} + + // D'tor. + virtual ~List(); + + // Clears the list. + void Clear() { + if ( size_ > 0 ) { + // 1. Deletes every node. + ListNode * node = head_; + ListNode * next = node->next(); + for ( ; ; ) { + delete node; + node = next; + if ( node == NULL ) break; + next = node->next(); + } + + // 2. Resets the member variables. + head_ = last_ = NULL; + size_ = 0; + } + } + + // Gets the number of elements. + int size() const { return size_; } + + // Returns true if the list is empty. + bool IsEmpty() const { return size() == 0; } + + // Gets the first element of the list, or NULL if the list is empty. + ListNode * Head() { return head_; } + const ListNode * Head() const { return head_; } + + // Gets the last element of the list, or NULL if the list is empty. + ListNode * Last() { return last_; } + const ListNode * Last() const { return last_; } + + // Adds an element to the end of the list. A copy of the element is + // created using the copy constructor, and then stored in the list. + // Changes made to the element in the list doesn't affect the source + // object, and vice versa. + void PushBack(const E & element) { + ListNode * new_node = new ListNode(element); + + if ( size_ == 0 ) { + head_ = last_ = new_node; + size_ = 1; + } else { + last_->next_ = new_node; + last_ = new_node; + size_++; + } + } + + // Adds an element to the beginning of this list. + void PushFront(const E& element) { + ListNode* const new_node = new ListNode(element); + + if ( size_ == 0 ) { + head_ = last_ = new_node; + size_ = 1; + } else { + new_node->next_ = head_; + head_ = new_node; + size_++; + } + } + + // Removes an element from the beginning of this list. If the + // result argument is not NULL, the removed element is stored in the + // memory it points to. Otherwise the element is thrown away. + // Returns true iff the list wasn't empty before the operation. + bool PopFront(E* result) { + if (size_ == 0) return false; + + if (result != NULL) { + *result = head_->element_; + } + + ListNode* const old_head = head_; + size_--; + if (size_ == 0) { + head_ = last_ = NULL; + } else { + head_ = head_->next_; + } + delete old_head; + + return true; + } + + // Inserts an element after a given node in the list. It's the + // caller's responsibility to ensure that the given node is in the + // list. If the given node is NULL, inserts the element at the + // front of the list. + ListNode* InsertAfter(ListNode* node, const E& element) { + if (node == NULL) { + PushFront(element); + return Head(); + } + + ListNode* const new_node = new ListNode(element); + new_node->next_ = node->next_; + node->next_ = new_node; + size_++; + if (node == last_) { + last_ = new_node; + } + + return new_node; + } + + // Returns the number of elements that satisfy a given predicate. + // The parameter 'predicate' is a Boolean function or functor that + // accepts a 'const E &', where E is the element type. + template // P is the type of the predicate function/functor + int CountIf(P predicate) const { + int count = 0; + for ( const ListNode * node = Head(); + node != NULL; + node = node->next() ) { + if ( predicate(node->element()) ) { + count++; + } + } + + return count; + } + + // Applies a function/functor to each element in the list. The + // parameter 'functor' is a function/functor that accepts a 'const + // E &', where E is the element type. This method does not change + // the elements. + template // F is the type of the function/functor + void ForEach(F functor) const { + for ( const ListNode * node = Head(); + node != NULL; + node = node->next() ) { + functor(node->element()); + } + } + + // Returns the first node whose element satisfies a given predicate, + // or NULL if none is found. The parameter 'predicate' is a + // function/functor that accepts a 'const E &', where E is the + // element type. This method does not change the elements. + template // P is the type of the predicate function/functor. + const ListNode * FindIf(P predicate) const { + for ( const ListNode * node = Head(); + node != NULL; + node = node->next() ) { + if ( predicate(node->element()) ) { + return node; + } + } + + return NULL; + } + + template + ListNode * FindIf(P predicate) { + for ( ListNode * node = Head(); + node != NULL; + node = node->next() ) { + if ( predicate(node->element() ) ) { + return node; + } + } + + return NULL; + } + + private: + ListNode* head_; // The first node of the list. + ListNode* last_; // The last node of the list. + int size_; // The number of elements in the list. + + // We disallow copying List. + GTEST_DISALLOW_COPY_AND_ASSIGN(List); +}; + +// The virtual destructor of List. +template +List::~List() { + Clear(); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T * x) { + delete x; +} + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const char* key, const char* value) : + key_(key), value_(value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const char* new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + String key_; + // The value supplied by the user. + String value_; +}; + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const char* key) + : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return String(test_property.key()).Compare(key_) == 0; + } + + private: + String key_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the list of TestPartResults. + const internal::List & test_part_results() const { + return test_part_results_; + } + + // Gets the list of TestProperties. + const internal::List & test_properties() const { + return test_properties_; + } + + // Gets the number of successful test parts. + int successful_part_count() const; + + // Gets the number of failed test parts. + int failed_part_count() const; + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns true iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const { return failed_part_count() > 0; } + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. + void RecordProperty(const internal::TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // TODO(russr): Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const internal::TestProperty& test_property); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the object. + void Clear(); + private: + // Protects mutable state of the property list and of owned properties, whose + // values may be updated. + internal::Mutex test_properites_mutex_; + + // The list of TestPartResults + internal::List test_part_results_; + // The list of TestProperties + internal::List test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN(TestResult); +}; // class TestResult + +class TestInfoImpl { + public: + TestInfoImpl(TestInfo* parent, const char* test_case_name, + const char* name, TypeId fixture_class_id, + TestMaker maker); + ~TestInfoImpl(); + + // Returns true if this test should run. + bool should_run() const { return should_run_; } + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Returns true if this test is disabled. Disabled tests are not run. + bool is_disabled() const { return is_disabled_; } + + // Sets the is_disabled member. + void set_is_disabled(bool is) { is_disabled_ = is; } + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the ID of the test fixture class. + TypeId fixture_class_id() const { return fixture_class_id_; } + + // Returns the test result. + internal::TestResult* result() { return &result_; } + const internal::TestResult* result() const { return &result_; } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Calls the given TestInfo object's Run() method. + static void RunTest(TestInfo * test_info) { + test_info->impl()->Run(); + } + + // Clears the test result. + void ClearResult() { result_.Clear(); } + + // Clears the test result in the given TestInfo object. + static void ClearTestResult(TestInfo * test_info) { + test_info->impl()->ClearResult(); + } + + private: + // These fields are immutable properties of the test. + TestInfo* const parent_; // The owner of this object + const String test_case_name_; // Test case name + const String name_; // Test name + const TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + const TestMaker maker_; // The function that creates the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + internal::TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(TestInfoImpl); +}; + +} // namespace internal + +// A test case, which consists of a list of TestInfos. +// +// TestCase is not copyable. +class TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Gets the (mutable) list of TestInfos in this TestCase. + internal::List& test_info_list() { return *test_info_list_; } + + // Gets the (immutable) list of TestInfos in this TestCase. + const internal::List & test_info_list() const { + return *test_info_list_; + } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + int failed_test_count() const; + + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; + + // Get the number of tests in this test case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + internal::TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Finds and returns a TestInfo with the given name. If one doesn't + // exist, returns NULL. + TestInfo* GetTestInfo(const char* test_name); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs every test in the given TestCase. + static void RunTestCase(TestCase * test_case) { test_case->Run(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo * test_info) { + const internal::TestInfoImpl* const impl = test_info->impl(); + return impl->should_run() && impl->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo * test_info) { + const internal::TestInfoImpl* const impl = test_info->impl(); + return impl->should_run() && impl->result()->Failed(); + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo * test_info) { + return test_info->impl()->is_disabled(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo *test_info) { + return test_info->impl()->should_run(); + } + + private: + // Name of the test case. + internal::String name_; + // List of TestInfos. + internal::List* test_info_list_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + internal::TimeInMillis elapsed_time_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN(TestCase); +}; + +namespace internal { + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static String GetOutputFormat(); + + // Returns the name of the requested output file, or the default if none + // was explicitly specified. + static String GetOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const String &test_case_name, + const String &test_name); + +#ifdef GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + private: + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const String& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as a String. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual String CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN(OsStackTraceGetterInterface); +}; + +// A working implemenation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() {} + virtual String CurrentStackTrace(int max_depth, int skip_count); + virtual void UponLeavingGTest(); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + String message; +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class UnitTestImpl : public TestPartResultReporterInterface { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // Reports a test part result. This method is from the + // TestPartResultReporterInterface interface. + virtual void ReportTestPartResult(const TestPartResult& result); + + // Returns the current test part result reporter. + TestPartResultReporterInterface* test_part_result_reporter(); + + // Sets the current test part result reporter. + void set_test_part_result_reporter(TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + internal::TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const internal::TestResult* ad_hoc_test_result() const { + return &ad_hoc_test_result_; + } + + // Sets the unit test result printer. + // + // Does nothing if the input and the current printer object are the + // same; otherwise, deletes the old printer object and makes the + // input the current printer. + void set_result_printer(UnitTestEventListenerInterface * result_printer); + + // Returns the current unit test result printer if it is not NULL; + // otherwise, creates an appropriate result printer, makes it the + // current printer, and returns it. + UnitTestEventListenerInterface* result_printer(); + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as a String. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + String CurrentOsStackTraceExceptTop(int skip_count); + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo * test_info) { + GetTestCase(test_info->test_case_name(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* current_test_case) { + current_test_case_ = current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* current_test_info) { + current_test_info_ = current_test_info; + } + + // Runs all tests in this UnitTest object, prints the result, and + // returns 0 if all tests are successful, or 1 otherwise. If any + // exception is thrown during a test on Windows, this test is + // considered to be failed, but the rest of the tests will still be + // run. (We disable exceptions on Linux and Mac OS X, so the issue + // doesn't apply there.) + int RunAllTests(); + + // Clears the results of all tests, including the ad hoc test. + void ClearResult() { + test_cases_.ForEach(TestCase::ClearTestCaseResult); + ad_hoc_test_result_.Clear(); + } + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // Returns the number of tests that should run. + int FilterTests(); + + // Lists all the tests by name. + void ListAllTests(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the list of environments that need to be set-up/torn-down + // before/after the tests are run. + internal::List* environments() { return &environments_; } + internal::List* environments_in_reverse_order() { + return &environments_in_reverse_order_; + } + + internal::List* test_cases() { return &test_cases_; } + const internal::List* test_cases() const { return &test_cases_; } + + // Getters for the per-thread Google Test trace stack. + internal::List* gtest_trace_stack() { + return gtest_trace_stack_.pointer(); + } + const internal::List* gtest_trace_stack() const { + return gtest_trace_stack_.pointer(); + } + +#ifdef GTEST_HAS_DEATH_TEST + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + private: + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // Points to (but doesn't own) the test part result reporter. + TestPartResultReporterInterface* test_part_result_reporter_; + + // The list of environments that need to be set-up/torn-down + // before/after the tests are run. environments_in_reverse_order_ + // simply mirrors environments_ in reverse order. + internal::List environments_; + internal::List environments_in_reverse_order_; + + internal::List test_cases_; // The list of TestCases. + + // Points to the last death test case registered. Initially NULL. + internal::ListNode* last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initally NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + internal::TestResult ad_hoc_test_result_; + + // The unit test result printer. Will be deleted when the UnitTest + // object is destructed. By default, a plain text printer is used, + // but the user can set this field to use a custom printer if that + // is desired. + UnitTestEventListenerInterface* result_printer_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#ifdef GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ diff --git a/src/gtest/gtest-message.h b/src/gtest/gtest-message.h new file mode 100644 index 00000000..746a1685 --- /dev/null +++ b/src/gtest/gtest-message.h @@ -0,0 +1,236 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-string.h" // NOLINT +#include "gtest-internal.h" // NOLINT +#else +#include +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a StrStream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that StrStream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + // We allocate the StrStream separately because it otherwise each use of + // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's + // stack frame leading to huge stack frames in some cases; gcc does not reuse + // the stack space. + Message() : ss_(new internal::StrStream) {} + + // Copy constructor. + Message(const Message& msg) : ss_(new internal::StrStream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new internal::StrStream) { + *ss_ << str; + } + + ~Message() { delete ss_; } +#ifdef __SYMBIAN32__ + // Streams a value (either a pointer or not) to this object. + template + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer::type(), value); + return *this; + } +#else + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + ::GTestStreamToHelper(ss_, val); + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + ::GTestStreamToHelper(ss_, pointer); + } + return *this; + } +#endif // __SYMBIAN32__ + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); + } + Message& operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); + } + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_WSTRING + + // Gets the text streamed to this object so far as a String. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::String GetString() const { + return internal::StrStreamToString(ss_); + } + + private: +#ifdef __SYMBIAN32__ + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template + inline void StreamHelper(internal::true_type dummy, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + ::GTestStreamToHelper(ss_, pointer); + } + } + template + inline void StreamHelper(internal::false_type dummy, const T& value) { + ::GTestStreamToHelper(ss_, value); + } +#endif // __SYMBIAN32__ + + // We'll hold the text streamed to this object here. + internal::StrStream* const ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/src/gtest/gtest-port.cc b/src/gtest/gtest-port.cc new file mode 100644 index 00000000..5c126149 --- /dev/null +++ b/src/gtest/gtest-port.cc @@ -0,0 +1,292 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) + +#include + +#include +#ifdef GTEST_HAS_DEATH_TEST +#include +#endif // GTEST_HAS_DEATH_TEST +#include +#include + +#include +#include +#include + +namespace testing { +namespace internal { + +#ifdef GTEST_HAS_DEATH_TEST + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + regfree(®ex_); + free(const_cast(pattern_)); +} + +// Returns true iff str contains regular expression re. +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = strdup(regex); + is_valid_ = regcomp(®ex_, regex, REG_EXTENDED) == 0; + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; +} + +#endif // GTEST_HAS_DEATH_TEST + +// Logs a message at the given severity level. +void GTestLog(GTestLogSeverity severity, const char* file, + int line, const char* msg) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + fprintf(stderr, "\n%s %s:%d: %s\n", marker, file, line, msg); + if (severity == GTEST_FATAL) { + abort(); + } +} + +#ifdef GTEST_HAS_DEATH_TEST + +// Defines the stderr capturer. + +class CapturedStderr { + public: + // The ctor redirects stderr to a temporary file. + CapturedStderr() { + uncaptured_fd_ = dup(STDERR_FILENO); + + char name_template[] = "captured_stderr.XXXXXX"; + const int captured_fd = mkstemp(name_template); + filename_ = name_template; + fflush(NULL); + dup2(captured_fd, STDERR_FILENO); + close(captured_fd); + } + + ~CapturedStderr() { + remove(filename_.c_str()); + } + + // Stops redirecting stderr. + void StopCapture() { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, STDERR_FILENO); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + // Returns the name of the temporary file holding the stderr output. + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + ::std::string filename() const { return filename_; } + + private: + int uncaptured_fd_; + ::std::string filename_; +}; + +static CapturedStderr* g_captured_stderr = NULL; + +// Returns the size (in bytes) of a file. +static size_t GetFileSize(FILE * file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +// Reads the entire content of a file as a string. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can +// use it here. +static ::std::string ReadEntireFile(FILE * file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const ::std::string content(buffer, buffer+bytes_read); + delete[] buffer; + + return content; +} + +// Starts capturing stderr. +void CaptureStderr() { + if (g_captured_stderr != NULL) { + GTEST_LOG(FATAL, "Only one stderr capturer can exist at one time."); + } + g_captured_stderr = new CapturedStderr; +} + +// Stops capturing stderr and returns the captured string. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can +// use it here. +::std::string GetCapturedStderr() { + g_captured_stderr->StopCapture(); + FILE* const file = fopen(g_captured_stderr->filename().c_str(), "r"); + const ::std::string content = ReadEntireFile(file); + fclose(file); + + delete g_captured_stderr; + g_captured_stderr = NULL; + + return content; +} + +// A copy of all command line arguments. Set by ParseGTestFlags(). +::std::vector g_argvs; + +// Returns the command line as a vector of strings. +const ::std::vector& GetArgvs() { return g_argvs; } + +#endif // GTEST_HAS_DEATH_TEST + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static String FlagToEnvVar(const char* flag) { + const String full_flag = (Message() << GTEST_FLAG_PREFIX << flag).GetString(); + + Message env_var; + for (int i = 0; i != full_flag.GetLength(); i++) { + env_var << static_cast(toupper(full_flag.c_str()[i])); + } + + return env_var.GetString(); +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const string_value = GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const string_value = GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const value = GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing diff --git a/src/gtest/gtest-spi.h b/src/gtest/gtest-spi.h new file mode 100644 index 00000000..75d0dcf2 --- /dev/null +++ b/src/gtest/gtest-spi.h @@ -0,0 +1,247 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class TestPartResult { + public: + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(TestPartResultType type, + const char* file_name, + int line_number, + const char* message) + : type_(type), + file_name_(file_name), + line_number_(line_number), + message_(message) { + } + + // Gets the outcome of the test part. + TestPartResultType type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { return file_name_.c_str(); } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true iff the test part passed. + bool passed() const { return type_ == TPRT_SUCCESS; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != TPRT_SUCCESS; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == TPRT_NONFATAL_FAILURE; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == TPRT_FATAL_FAILURE; } + private: + TestPartResultType type_; + + // The name of the source file where the test part took place, or + // NULL if the source file is unknown. + internal::String file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + internal::String message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// We define this class as we cannot use STL containers when compiling +// Google Test with MSVC 7.1 and exceptions disabled. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class TestPartResultArray { + public: + TestPartResultArray(); + ~TestPartResultArray(); + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + private: + // Internally we use a list to simulate the array. Yes, this means + // that random access is O(N) in time, but it's OK for its purpose. + internal::List* const list_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a +// Google Test failure is reported. +class ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + TestPartResultReporterInterface* const old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResultType type, + const char* substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResultType type_; + const String substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// Implementation note: The verification is done in the destructor of +// SingleFailureChecker, to make sure that it's done even when +// 'statement' throws an exception. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +#define EXPECT_FATAL_FAILURE(statement, substr) do {\ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TPRT_FATAL_FAILURE, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (false) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with +// 'substr' being part of the failure message. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// Implementation note: The verification is done in the destructor of +// SingleFailureChecker, to make sure that it's done even when +// 'statement' throws an exception or aborts the function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +#define EXPECT_NONFATAL_FAILURE(statement, substr) do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TPRT_NONFATAL_FAILURE, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + >est_failures);\ + statement;\ + }\ + } while (false) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/src/gtest/gtest.cc b/src/gtest/gtest.cc new file mode 100644 index 00000000..1eee3922 --- /dev/null +++ b/src/gtest/gtest.cc @@ -0,0 +1,3546 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef GTEST_OS_LINUX + +// TODO(kenton): Use autoconf to detect availability of gettimeofday(). +#define HAS_GETTIMEOFDAY + +#include +#include +#include +// Declares vsnprintf(). This header is not available on Windows. +#include +#include +#include +#include +#include +#include + +#elif defined(_WIN32_WCE) // We are on Windows CE. + +#include // NOLINT + +#elif defined(_WIN32) // We are on Windows proper. + +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#if defined(__MINGW__) || defined(__MINGW32__) +// MinGW has gettimeofday() but not _ftime64() +// TODO(kenton): Use autoconf to detect availability of gettimeofday(). +// TODO(kenton): There are other ways to get the time on Windows, like +// GetTickCount() or GetSystemTimeAsFileTime(). MinGW supports these. +// consider using them instead. +#define HAS_GETTIMEOFDAY +#include // NOLINT +#endif + +// cpplint thinks that the header is already included, so we want to +// silence it. +#include // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton): Use autoconf to detect availability of gettimeofday(). +#define HAS_GETTIMEOFDAY + +// cpplint thinks that the header is already included, so we want to +// silence it. +#include // NOLINT +#include // NOLINT + +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION +#include +#undef GTEST_IMPLEMENTATION + +#ifdef GTEST_OS_WINDOWS +#define fileno _fileno +#define isatty _isatty +#define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +// Constants. + +// A test that matches this pattern is disabled and not run. +static const char kDisableTestPattern[] = "DISABLED_*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +GTEST_DEFINE_bool( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", false), + "True iff " GTEST_NAME + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to xterm or xterm-color."); + +GTEST_DEFINE_string( + filter, + internal::StringFromGTestEnv("filter", kUniversalFilter), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_int32( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_int32( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_bool( + show_internal_stack_frames, false, + "True iff " GTEST_NAME " should include internal stack frames when " + "printing test failure stack traces."); + +namespace internal { + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). + +// A user must call testing::ParseGTestFlags() to initialize Google +// Test. g_parse_gtest_flags_called is set to true iff +// ParseGTestFlags() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +static bool g_parse_gtest_flags_called = false; +static bool GTestIsInitialized() { return g_parse_gtest_flags_called; } + +// Iterates over a list of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const internal::List& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (const internal::ListNode* node = case_list.Head(); + node != NULL; + node = node->next()) { + sum += (node->element()->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +#ifdef _WIN32_WCE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +static void abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +#endif // _WIN32_WCE + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResultType type, const char* file, + int line, const char* message) + : type_(type), file_(file), line_(line), message_(message) { +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(type_, file_, line_, + AppendUserMessage(message_, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Application pathname gotten in ParseGTestFlags. +String g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if defined(_WIN32_WCE) || defined(_WIN32) + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // _WIN32_WCE || _WIN32 + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +String UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return String(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + String(gtest_output_flag) : + String(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +String UnitTestOptions::GetOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return String(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return String(kDefaultOutputFile); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsDirectory()) + return output_name.ToString(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.ToString(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { + const char *cur_pattern = filter; + while (true) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// TODO(keithray): move String function implementations to gtest-string.cc. + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, + const String &test_name) { + const String& full_name = String::Format("%s.%s", + test_case_name.c_str(), + test_name.c_str()); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + String positive; + String negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = String(""); + } else { + positive.Set(p, dash - p); // Everything up to the dash + negative = String(dash+1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#ifdef GTEST_OS_WINDOWS +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle an exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception. + return (GTEST_FLAG(catch_exceptions) && + exception_code != EXCEPTION_BREAKPOINT) ? + EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_OS_WINDOWS + +} // namespace internal + +// The interface for printing the result of a UnitTest +class UnitTestEventListenerInterface { + public: + // The d'tor is pure virtual as this is an abstract class. + virtual ~UnitTestEventListenerInterface() = 0; + + // Called before the unit test starts. + virtual void OnUnitTestStart(const UnitTest*) {} + + // Called after the unit test ends. + virtual void OnUnitTestEnd(const UnitTest*) {} + + // Called before the test case starts. + virtual void OnTestCaseStart(const TestCase*) {} + + // Called after the test case ends. + virtual void OnTestCaseEnd(const TestCase*) {} + + // Called before the global set-up starts. + virtual void OnGlobalSetUpStart(const UnitTest*) {} + + // Called after the global set-up ends. + virtual void OnGlobalSetUpEnd(const UnitTest*) {} + + // Called before the global tear-down starts. + virtual void OnGlobalTearDownStart(const UnitTest*) {} + + // Called after the global tear-down ends. + virtual void OnGlobalTearDownEnd(const UnitTest*) {} + + // Called before the test starts. + virtual void OnTestStart(const TestInfo*) {} + + // Called after the test ends. + virtual void OnTestEnd(const TestInfo*) {} + + // Called after an assertion. + virtual void OnNewTestPartResult(const TestPartResult*) {} +}; + +// Constructs an empty TestPartResultArray. +TestPartResultArray::TestPartResultArray() + : list_(new internal::List) { +} + +// Destructs a TestPartResultArray. +TestPartResultArray::~TestPartResultArray() { + delete list_; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + list_->PushBack(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + abort(); + } + + const internal::ListNode* p = list_->Head(); + for (int i = 0; i < index; i++) { + p = p->next(); + } + + return p->element(); +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return list_->size(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : old_reporter_(UnitTest::GetInstance()->impl()-> + test_part_result_reporter()), + result_(result) { + internal::UnitTestImpl* const impl = UnitTest::GetInstance()->impl(); + impl->set_test_part_result_reporter(this); +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + UnitTest::GetInstance()->impl()-> + set_test_part_result_reporter(old_reporter_); +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResultType type, + const char* substr) { + const String expected( + type == TPRT_FATAL_FAILURE ? "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure(msg); + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + msg << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + return AssertionFailure(msg); + } + + if (strstr(r.message(), substr) == NULL) { + msg << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + return AssertionFailure(msg); + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResultType type, + const char* substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_.c_str()); +} + +// Reports a test part result. +void UnitTestImpl::ReportTestPartResult(const TestPartResult& result) { + current_test_result()->AddTestPartResult(result); + result_printer()->OnNewTestPartResult(&result); +} + +// Returns the current test part result reporter. +TestPartResultReporterInterface* UnitTestImpl::test_part_result_reporter() { + return test_part_result_reporter_; +} + +// Sets the current test part result reporter. +void UnitTestImpl::set_test_part_result_reporter( + TestPartResultReporterInterface* reporter) { + test_part_result_reporter_ = reporter; +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return test_cases_.CountIf(TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return test_cases_.CountIf(TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return test_cases_.size(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return test_cases_.CountIf(ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as a String. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return String(""); +} + +static TimeInMillis GetTimeInMillis() { +#ifdef _WIN32_WCE // We are on Windows CE + // Difference between 1970-01-01 and 1601-01-01 in miliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = 11644473600000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton): Shouldn't this just use GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif defined(_WIN32) && !defined(HAS_GETTIMEOFDAY) + __timeb64 now; +#ifdef _MSC_VER + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton): Use GetTickCount()? Or use SystemTimeToFileTime() +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +#pragma warning(pop) // Restores the warning state. +#else + _ftime64(&now); +#endif // _MSC_VER + return static_cast(now.time) * 1000 + now.millitm; +#elif defined(HAS_GETTIMEOFDAY) + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +#error "Don't know how to get the current time on your system." + return 0; +#endif +} + +// Utilities + +// class String + +// Returns the input enclosed in double quotes if it's not NULL; +// otherwise returns "(null)". For example, "\"Hello\"" is returned +// for input "Hello". +// +// This is useful for printing a C string in the syntax of a literal. +// +// Known issue: escape sequences are not handled yet. +String String::ShowCStringQuoted(const char* c_str) { + return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); +} + +// Copies at most length characters from str into a newly-allocated +// piece of memory of size length+1. The memory is allocated with new[]. +// A terminating null byte is written to the memory, and a pointer to it +// is returned. If str is NULL, NULL is returned. +static char* CloneString(const char* str, size_t length) { + if (str == NULL) { + return NULL; + } else { + char* const clone = new char[length + 1]; + // MSVC 8 deprecates strncpy(), so we want to suppress warning + // 4996 (deprecated function) there. +#ifdef GTEST_OS_WINDOWS // We are on Windows. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + strncpy(clone, str, length); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + strncpy(clone, str, length); +#endif // GTEST_OS_WINDOWS + clone[length] = '\0'; + return clone; + } +} + +// Clones a 0-terminated C string, allocating memory using new. The +// caller is responsible for deleting[] the return value. Returns the +// cloned string, or NULL if the input is NULL. +const char * String::CloneCString(const char* c_str) { + return (c_str == NULL) ? + NULL : CloneString(c_str, strlen(c_str)); +} + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t len, + Message* msg) { + for (size_t i = 0; i != len; i++) { + // TODO(wan): consider allowing a testing::String object to + // contain '\0'. This will make it behave more like std::string, + // and will allow ToUtf8String() to return the correct encoding + // for '\0' s.t. we can get rid of the conditional here (and in + // several other places). + if (wstr[i]) { + *msg << internal::ToUtf8String(wstr[i]); + } else { + *msg << '\0'; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +namespace internal { + +// Formats a value to be used in a failure message. + +// For a char value, we print it as a C++ char literal and as an +// unsigned integer (both in decimal and in hexadecimal). +String FormatForFailureMessage(char ch) { + const unsigned int ch_as_uint = ch; + // A String object cannot contain '\0', so we print "\\0" when ch is + // '\0'. + return String::Format("'%s' (%u, 0x%X)", + ch ? String::Format("%c", ch).c_str() : "\\0", + ch_as_uint, ch_as_uint); +} + +// For a wchar_t value, we print it as a C++ wchar_t literal and as an +// unsigned integer (both in decimal and in hexidecimal). +String FormatForFailureMessage(wchar_t wchar) { + // The C++ standard doesn't specify the exact size of the wchar_t + // type. It just says that it shall have the same size as another + // integral type, called its underlying type. + // + // Therefore, in order to print a wchar_t value in the numeric form, + // we first convert it to the largest integral type (UInt64) and + // then print the converted value. + // + // We use streaming to print the value as "%llu" doesn't work + // correctly with MSVC 7.1. + const UInt64 wchar_as_uint64 = wchar; + Message msg; + // A String object cannot contain '\0', so we print "\\0" when wchar is + // L'\0'. + msg << "L'" << (wchar ? ToUtf8String(wchar).c_str() : "\\0") << "' (" + << wchar_as_uint64 << ", 0x" << ::std::setbase(16) + << wchar_as_uint64 << ")"; + return msg.GetString(); +} + +} // namespace internal + +// AssertionResult constructor. +AssertionResult::AssertionResult(const internal::String& failure_message) + : failure_message_(failure_message) { +} + + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(); +} + + +// Makes a failed assertion result with the given failure message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionResult(message.GetString()); +} + +namespace internal { + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const String& expected_value, + const String& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure(msg); +} + + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + Message msg; + msg << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; + return AssertionFailure(msg); +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + StrStream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + StrStream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + Message msg; + msg << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StrStreamToString(&val1_ss) << " vs " + << StrStreamToString(&val2_ss); + + return AssertionFailure(msg); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + Message msg;\ + msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + return AssertionFailure(msg);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowCStringQuoted(expected), + String::ShowCStringQuoted(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowCStringQuoted(expected), + String::ShowCStringQuoted(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + Message msg; + msg << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + return AssertionFailure(msg); + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + Message msg; + msg << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + return AssertionFailure(msg); + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure( + Message() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""); +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_STRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_STRING + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#ifdef GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +#ifdef _WIN32_WCE + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; +#else + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; // String::Format can't exceed this length. + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing cr-lf) + for (; message_length && isspace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } +#endif // _WIN32_WCE + + const String error_hex(String::Format("0x%08X ", hr)); + Message msg; + msg << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << error_text << "\n"; + + return ::testing::AssertionFailure(msg); +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code-point to its UTF-8 encoding. +String ToUtf8String(wchar_t wchar) { + char str[5] = {}; // Initializes str to all '\0' characters. + + UInt32 code = static_cast(wchar); + if (code <= kMaxCodePoint1) { + str[0] = static_cast(code); // 0xxxxxxx + } else if (code <= kMaxCodePoint2) { + str[1] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code); // 110xxxxx + } else if (code <= kMaxCodePoint3) { + str[2] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code); // 1110xxxx + } else if (code <= kMaxCodePoint4) { + str[3] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code); // 11110xxx + } else { + return String::Format("(Invalid Unicode 0x%llX)", + static_cast(wchar)); + } + + return String(str); +} + +// Converts a wide C string to a String using the UTF-8 encoding. +// NULL will be converted to "(null)". +String String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return String("(null)"); + + StrStream ss; + while (*wide_c_str) { + ss << internal::ToUtf8String(*wide_c_str++); + } + + return internal::StrStreamToString(&ss); +} + +// Similar to ShowWideCString(), except that this function encloses +// the converted string in double quotes. +String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { + if (wide_c_str == NULL) return String("(null)"); + + return String::Format("L\"%s\"", + String::ShowWideCString(wide_c_str).c_str()); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowWideCStringQuoted(expected), + String::ShowWideCStringQuoted(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + Message msg; + msg << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << String::ShowWideCStringQuoted(s1) + << " vs " << String::ShowWideCStringQuoted(s2); + return AssertionFailure(msg); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + +#ifdef GTEST_OS_WINDOWS + return _stricmp(lhs, rhs) == 0; +#else // GTEST_OS_WINDOWS + return strcasecmp(lhs, rhs) == 0; +#endif // GTEST_OS_WINDOWS +} + +// Constructs a String by copying a given number of chars from a +// buffer. E.g. String("hello", 3) will create the string "hel". +String::String(const char * buffer, size_t len) { + char * const temp = new char[ len + 1 ]; + memcpy(temp, buffer, len); + temp[ len ] = '\0'; + c_str_ = temp; +} + +// Compares this with another String. +// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 +// if this is greater than rhs. +int String::Compare(const String & rhs) const { + if ( c_str_ == NULL ) { + return rhs.c_str_ == NULL ? 0 : -1; // NULL < anything except NULL + } + + return rhs.c_str_ == NULL ? 1 : strcmp(c_str_, rhs.c_str_); +} + +// Returns true iff this String ends with the given suffix. *Any* +// String is considered to end with a NULL or empty suffix. +bool String::EndsWith(const char* suffix) const { + if (suffix == NULL || CStringEquals(suffix, "")) return true; + + if (c_str_ == NULL) return false; + + const size_t this_len = strlen(c_str_); + const size_t suffix_len = strlen(suffix); + return (this_len >= suffix_len) && + CStringEquals(c_str_ + this_len - suffix_len, suffix); +} + +// Returns true iff this String ends with the given suffix, ignoring case. +// Any String is considered to end with a NULL or empty suffix. +bool String::EndsWithCaseInsensitive(const char* suffix) const { + if (suffix == NULL || CStringEquals(suffix, "")) return true; + + if (c_str_ == NULL) return false; + + const size_t this_len = strlen(c_str_); + const size_t suffix_len = strlen(suffix); + return (this_len >= suffix_len) && + CaseInsensitiveCStringEquals(c_str_ + this_len - suffix_len, suffix); +} + +// Sets the 0-terminated C string this String object represents. The +// old string in this object is deleted, and this object will own a +// clone of the input string. This function copies only up to length +// bytes (plus a terminating null byte), or until the first null byte, +// whichever comes first. +// +// This function works even when the c_str parameter has the same +// value as that of the c_str_ field. +void String::Set(const char * c_str, size_t length) { + // Makes sure this works when c_str == c_str_ + const char* const temp = CloneString(c_str, length); + delete[] c_str_; + c_str_ = temp; +} + +// Assigns a C string to this object. Self-assignment works. +const String& String::operator=(const char* c_str) { + // Makes sure this works when c_str == c_str_ + if (c_str != c_str_) { + delete[] c_str_; + c_str_ = CloneCString(c_str); + } + return *this; +} + +// Formats a list of arguments to a String, using the same format +// spec string as for printf. +// +// We do not use the StringPrintf class as it is not universally +// available. +// +// The result is limited to 4096 characters (including the tailing 0). +// If 4096 characters are not enough to format the input, +// "" is returned. +String String::Format(const char * format, ...) { + va_list args; + va_start(args, format); + + char buffer[4096]; + // MSVC 8 deprecates vsnprintf(), so we want to suppress warning + // 4996 (deprecated function) there. +#ifdef GTEST_OS_WINDOWS // We are on Windows. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + const int size = + vsnprintf(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, format, args); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + const int size = + vsnprintf(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, format, args); +#endif // GTEST_OS_WINDOWS + va_end(args); + + return String(size >= 0 ? buffer : ""); +} + +// Converts the buffer in a StrStream to a String, converting NUL +// bytes to "\\0" along the way. +String StrStreamToString(StrStream* ss) { +#if GTEST_HAS_STD_STRING + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); +#else + const char* const start = ss->str(); + const char* const end = start + ss->pcount(); +#endif // GTEST_HAS_STD_STRING + + // We need to use a helper StrStream to do this transformation + // because String doesn't support push_back(). + StrStream helper; + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + helper << "\\0"; // Replaces NUL with "\\0"; + } else { + helper.put(*ch); + } + } + +#if GTEST_HAS_STD_STRING + return String(helper.str().c_str()); +#else + const String str(helper.str(), helper.pcount()); + helper.freeze(false); + ss->freeze(false); + return str; +#endif // GTEST_HAS_STD_STRING +} + +// Appends the user-supplied message to the Google-Test-generated message. +String AppendUserMessage(const String& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const String user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + Message msg; + msg << gtest_msg << "\n" << user_msg_string; + + return msg.GetString(); +} + +} // namespace internal + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os << result.file_name() << ":" + << result.line_number() << ": " + << (result.type() == TPRT_SUCCESS ? "Success" : + result.type() == TPRT_FATAL_FAILURE ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +namespace internal { +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.PushBack(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const TestProperty& test_property) { + if (!ValidateTestProperty(test_property)) { + return; + } + MutexLock lock(&test_properites_mutex_); + ListNode* const node_with_matching_key = + test_properties_.FindIf(TestPropertyKeyIs(test_property.key())); + if (node_with_matching_key == NULL) { + test_properties_.PushBack(test_property); + return; + } + TestProperty& property_with_matching_key = node_with_matching_key->element(); + property_with_matching_key.SetValue(test_property.value()); +} + +// Adds a failure if the key is a reserved attribute of Google Test testcase tags. +// Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const TestProperty& test_property) { + String key(test_property.key()); + if (key == "name" || key == "status" || key == "time" || key == "classname") { + ADD_FAILURE() + << "Reserved key used in RecordProperty(): " + << key + << " ('name', 'status', 'time', and 'classname' are reserved by " + << GTEST_NAME << ")"; + return false; + } + return true; +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.Clear(); + test_properties_.Clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test part passed. +static bool TestPartPassed(const TestPartResult & result) { + return result.passed(); +} + +// Gets the number of successful test parts. +int TestResult::successful_part_count() const { + return test_part_results_.CountIf(TestPartPassed); +} + +// Returns true iff the test part failed. +static bool TestPartFailed(const TestPartResult & result) { + return result.failed(); +} + +// Gets the number of failed test parts. +int TestResult::failed_part_count() const { + return test_part_results_.CountIf(TestPartFailed); +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult & result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return test_part_results_.CountIf(TestPartFatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return test_part_results_.size(); +} + +} // namespace internal + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const char* key, const char* value) { + UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const char* key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +#ifdef GTEST_OS_WINDOWS +// We are on Windows. + +// Adds an "exception thrown" fatal failure to the current test. +static void AddExceptionThrownFailure(DWORD exception_code, + const char* location) { + Message message; + message << "Exception thrown with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " in " << location << "."; + + UnitTest* const unit_test = UnitTest::GetInstance(); + unit_test->AddTestPartResult( + TPRT_FATAL_FAILURE, + static_cast(NULL), + // We have no info about the source file where the exception + // occurred. + -1, // We have no info on which line caused the exception. + message.GetString(), + internal::String("")); +} + +#endif // GTEST_OS_WINDOWS + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const internal::TestInfoImpl* const first_test_info = + test_case->test_info_list().Head()->element()->impl(); + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id(); + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const internal::TestInfoImpl* const this_test_info = + impl->current_test_info()->impl(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id(); + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); +#ifdef GTEST_OS_WINDOWS + // We are on Windows. + impl->os_stack_trace_getter()->UponLeavingGTest(); + __try { + SetUp(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), "SetUp()"); + } + + // We will run the test only if SetUp() had no fatal failure. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + __try { + TestBody(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), "the test body"); + } + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + __try { + TearDown(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), "TearDown()"); + } + +#else // We are on Linux or Mac - exceptions are disabled. + impl->os_stack_trace_getter()->UponLeavingGTest(); + SetUp(); + + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + TestBody(); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + TearDown(); +#endif // GTEST_OS_WINDOWS +} + + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. +TestInfo::TestInfo(const char* test_case_name, + const char* name, + internal::TypeId fixture_class_id, + TestMaker maker) { + impl_ = new internal::TestInfoImpl(this, test_case_name, name, + fixture_class_id, maker); +} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { + delete impl_; +} + +// Creates a TestInfo object and registers it with the UnitTest +// singleton; returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// maker: pointer to the function that creates a test object +TestInfo* TestInfo::MakeAndRegisterInstance( + const char* test_case_name, + const char* name, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestMaker maker) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, fixture_class_id, maker); + internal::GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +// Returns the test case name. +const char* TestInfo::test_case_name() const { + return impl_->test_case_name(); +} + +// Returns the test name. +const char* TestInfo::name() const { + return impl_->name(); +} + +// Returns true if this test should run. +bool TestInfo::should_run() const { return impl_->should_run(); } + +// Returns the result of the test. +const internal::TestResult* TestInfo::result() const { return impl_->result(); } + +// Increments the number of death tests encountered in this test so +// far. +int TestInfo::increment_death_test_count() { + return impl_->result()->increment_death_test_count(); +} + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && internal::String(test_info->name()).Compare(name_) == 0; + } + + private: + internal::String name_; +}; + +} // namespace + +// Finds and returns a TestInfo with the given name. If one doesn't +// exist, returns NULL. +TestInfo * TestCase::GetTestInfo(const char* test_name) { + // Can we find a TestInfo with the given name? + internal::ListNode * const node = test_info_list_->FindIf( + TestNameIs(test_name)); + + // Returns the TestInfo found. + return node ? node->element() : NULL; +} + +namespace internal { + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfoImpl::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(parent_); + + // Notifies the unit test event listener that a test is about to + // start. + UnitTestEventListenerInterface* const result_printer = + impl->result_printer(); + result_printer->OnTestStart(parent_); + + const TimeInMillis start = GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); +#ifdef GTEST_OS_WINDOWS + // We are on Windows. + Test* test = NULL; + + __try { + // Creates the test object. + test = (*maker_)(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), + "the test fixture's constructor"); + return; + } +#else // We are on Linux or Mac OS - exceptions are disabled. + + // TODO(wan): If test->Run() throws, test won't be deleted. This is + // not a problem now as we don't use exceptions. If we were to + // enable exceptions, we should revise the following to be + // exception-safe. + + // Creates the test object. + Test* test = (*maker_)(); +#endif // GTEST_OS_WINDOWS + + // Runs the test only if the constructor of the test fixture didn't + // generate a fatal failure. + if (!Test::HasFatalFailure()) { + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + delete test; + test = NULL; + + result_.set_elapsed_time(GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + result_printer->OnTestEnd(parent_); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +} // namespace internal + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return test_info_list_->CountIf(TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return test_info_list_->CountIf(TestFailed); +} + +int TestCase::disabled_test_count() const { + return test_info_list_->CountIf(TestDisabled); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return test_info_list_->CountIf(ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return test_info_list_->size(); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(name), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { + test_info_list_ = new internal::List; +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + test_info_list_->ForEach(internal::Delete); + + // Then deletes the Test collection. + delete test_info_list_; + test_info_list_ = NULL; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_->PushBack(test_info); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + UnitTestEventListenerInterface * const result_printer = + impl->result_printer(); + + result_printer->OnTestCaseStart(this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + set_up_tc_(); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + test_info_list_->ForEach(internal::TestInfoImpl::RunTest); + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + tear_down_tc_(); + result_printer->OnTestCaseEnd(this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + test_info_list_->ForEach(internal::TestInfoImpl::ClearTestResult); +} + + +// class UnitTestEventListenerInterface + +// The virtual d'tor. +UnitTestEventListenerInterface::~UnitTestEventListenerInterface() { +} + +// A result printer that never prints anything. Used in the child process +// of an exec-style death test to avoid needless output clutter. +class NullUnitTestResultPrinter : public UnitTestEventListenerInterface {}; + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static internal::String FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::String::Format("%d %s", count, + count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static internal::String FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static internal::String FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResultType enum to human-friendly string +// representation. Both TPRT_NONFATAL_FAILURE and TPRT_FATAL_FAILURE +// are translated to "Failure", as the user usually doesn't care about +// the difference between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResultType type) { + switch (type) { + case TPRT_SUCCESS: + return "Success"; + + case TPRT_NONFATAL_FAILURE: + case TPRT_FATAL_FAILURE: + return "Failure"; + } + + return "Unknown result type"; +} + +// Prints a TestPartResult. +static void PrintTestPartResult( + const TestPartResult & test_part_result) { + const char * const file_name = test_part_result.file_name(); + + printf("%s", file_name == NULL ? "unknown file" : file_name); + if (test_part_result.line_number() >= 0) { + printf(":%d", test_part_result.line_number()); + } + printf(": %s\n", TestPartResultTypeToString(test_part_result.type())); + printf("%s\n", test_part_result.message()); + fflush(stdout); +} + +// class PrettyUnitTestResultPrinter + +namespace internal { + +enum GTestColor { + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#ifdef _WIN32 + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + } + return 0; +} + +#else + +// Returns the ANSI color code for the given color. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + }; + return NULL; +} + +#endif // _WIN32 + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#ifdef _WIN32 + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // _WIN32 + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + static const bool use_color = ShouldUseColor(isatty(fileno(stdout)) != 0); + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#ifdef _WIN32 + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // _WIN32 + va_end(args); +} + +} // namespace internal + +using internal::ColoredPrintf; +using internal::COLOR_RED; +using internal::COLOR_GREEN; +using internal::COLOR_YELLOW; + +// This class implements the UnitTestEventListenerInterface interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public UnitTestEventListenerInterface { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the + // UnitTestEventListenerInterface class. + virtual void OnUnitTestStart(const UnitTest * unit_test); + virtual void OnGlobalSetUpStart(const UnitTest*); + virtual void OnTestCaseStart(const TestCase * test_case); + virtual void OnTestStart(const TestInfo * test_info); + virtual void OnNewTestPartResult(const TestPartResult * result); + virtual void OnTestEnd(const TestInfo * test_info); + virtual void OnGlobalTearDownStart(const UnitTest*); + virtual void OnUnitTestEnd(const UnitTest * unit_test); + + private: + internal::String test_case_name_; +}; + +// Called before the unit test starts. +void PrettyUnitTestResultPrinter::OnUnitTestStart( + const UnitTest * unit_test) { + const char * const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!internal::String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME, filter); + } + + const internal::UnitTestImpl* const impl = unit_test->impl(); + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(impl->test_to_run_count()).c_str(), + FormatTestCaseCount(impl->test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnGlobalSetUpStart(const UnitTest*) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart( + const TestCase * test_case) { + test_case_name_ = test_case->name(); + const internal::String counts = + FormatCountableNoun(test_case->test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s\n", counts.c_str(), test_case_name_.c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo * test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_case_name_.c_str(), test_info->name()); + printf("\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo * test_info) { + if (test_info->result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_case_name_.c_str(), test_info->name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnNewTestPartResult( + const TestPartResult * result) { + // If the test part succeeded, we don't need to do anything. + if (result->type() == TPRT_SUCCESS) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(*result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnGlobalTearDownStart(const UnitTest*) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +namespace internal { + +// Internal helper for printing the list of failed tests. +static void PrintFailedTestsPretty(const UnitTestImpl* impl) { + const int failed_test_count = impl->failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (const internal::ListNode* node = impl->test_cases()->Head(); + node != NULL; node = node->next()) { + const TestCase* const tc = node->element(); + if (!tc->should_run() || (tc->failed_test_count() == 0)) { + continue; + } + for (const internal::ListNode* tinode = + tc->test_info_list().Head(); + tinode != NULL; tinode = tinode->next()) { + const TestInfo* const ti = tinode->element(); + if (!tc->ShouldRunTest(ti) || tc->TestPassed(ti)) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s\n", ti->test_case_name(), ti->name()); + } + } +} + +} // namespace internal + +void PrettyUnitTestResultPrinter::OnUnitTestEnd( + const UnitTest * unit_test) { + const internal::UnitTestImpl* const impl = unit_test->impl(); + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.\n", + FormatTestCount(impl->test_to_run_count()).c_str(), + FormatTestCaseCount(impl->test_case_to_run_count()).c_str()); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(impl->successful_test_count()).c_str()); + + int num_failures = impl->failed_test_count(); + if (!impl->Passed()) { + const int failed_test_count = impl->failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + internal::PrintFailedTestsPretty(impl); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = impl->disabled_test_count(); + if (num_disabled) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class UnitTestEventsRepeater +// +// This class forwards events to other event listeners. +class UnitTestEventsRepeater : public UnitTestEventListenerInterface { + public: + typedef internal::List Listeners; + typedef internal::ListNode ListenersNode; + UnitTestEventsRepeater() {} + virtual ~UnitTestEventsRepeater(); + void AddListener(UnitTestEventListenerInterface *listener); + + virtual void OnUnitTestStart(const UnitTest* unit_test); + virtual void OnUnitTestEnd(const UnitTest* unit_test); + virtual void OnGlobalSetUpStart(const UnitTest* unit_test); + virtual void OnGlobalSetUpEnd(const UnitTest* unit_test); + virtual void OnGlobalTearDownStart(const UnitTest* unit_test); + virtual void OnGlobalTearDownEnd(const UnitTest* unit_test); + virtual void OnTestCaseStart(const TestCase* test_case); + virtual void OnTestCaseEnd(const TestCase* test_case); + virtual void OnTestStart(const TestInfo* test_info); + virtual void OnTestEnd(const TestInfo* test_info); + virtual void OnNewTestPartResult(const TestPartResult* result); + + private: + Listeners listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(UnitTestEventsRepeater); +}; + +UnitTestEventsRepeater::~UnitTestEventsRepeater() { + for (ListenersNode* listener = listeners_.Head(); + listener != NULL; + listener = listener->next()) { + delete listener->element(); + } +} + +void UnitTestEventsRepeater::AddListener( + UnitTestEventListenerInterface *listener) { + listeners_.PushBack(listener); +} + +// Since the methods are identical, use a macro to reduce boilerplate. +// This defines a member that repeats the call to all listeners. +#define GTEST_REPEATER_METHOD(Name, Type) \ +void UnitTestEventsRepeater::Name(const Type* parameter) { \ + for (ListenersNode* listener = listeners_.Head(); \ + listener != NULL; \ + listener = listener->next()) { \ + listener->element()->Name(parameter); \ + } \ +} + +GTEST_REPEATER_METHOD(OnUnitTestStart, UnitTest) +GTEST_REPEATER_METHOD(OnUnitTestEnd, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalSetUpStart, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalSetUpEnd, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalTearDownStart, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalTearDownEnd, UnitTest) +GTEST_REPEATER_METHOD(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD(OnTestCaseEnd, TestCase) +GTEST_REPEATER_METHOD(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD(OnTestEnd, TestInfo) +GTEST_REPEATER_METHOD(OnNewTestPartResult, TestPartResult) + +#undef GTEST_REPEATER_METHOD + +// End PrettyUnitTestResultPrinter + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public UnitTestEventListenerInterface { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnUnitTestEnd(const UnitTest* unit_test); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static internal::String EscapeXml(const char* str, + bool is_attribute); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static internal::String EscapeXmlAttribute(const char* str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static internal::String EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Prints an XML representation of a TestInfo object. + static void PrintXmlTestInfo(FILE* out, + const char* test_case_name, + const TestInfo* test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(FILE* out, const TestCase* test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(FILE* out, const UnitTest* unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the String is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static internal::String TestPropertiesAsXmlAttributes( + const internal::TestResult* result); + + // The output file. + const internal::String output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnUnitTestEnd(const UnitTest* unit_test) { + FILE* xmlout = NULL; + internal::FilePath output_file(output_file_); + internal::FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + // MSVC 8 deprecates fopen(), so we want to suppress warning 4996 + // (deprecated function) there. +#ifdef GTEST_OS_WINDOWS + // We are on Windows. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + xmlout = fopen(output_file_.c_str(), "w"); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + xmlout = fopen(output_file_.c_str(), "w"); +#endif // GTEST_OS_WINDOWS + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + PrintXmlUnitTest(xmlout, unit_test); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +internal::String XmlUnitTestResultPrinter::EscapeXml(const char* str, + bool is_attribute) { + Message m; + + if (str != NULL) { + for (const char* src = str; *src; ++src) { + switch (*src) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(*src)) { + if (is_attribute && IsNormalizableWhitespace(*src)) + m << internal::String::Format("&#x%02X;", unsigned(*src)); + else + m << *src; + } + break; + } + } + } + + return m.GetString(); +} + + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// +// <-- individual assertion failures +// +// +// +// + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::PrintXmlTestInfo(FILE* out, + const char* test_case_name, + const TestInfo* test_info) { + const internal::TestResult * const result = test_info->result(); + const internal::List &results = result->test_part_results(); + fprintf(out, + " name()).c_str(), + test_info->should_run() ? "run" : "notrun", + internal::StreamableToString(result->elapsed_time()).c_str(), + EscapeXmlAttribute(test_case_name).c_str(), + TestPropertiesAsXmlAttributes(result).c_str()); + + int failures = 0; + for (const internal::ListNode* part_node = results.Head(); + part_node != NULL; + part_node = part_node->next()) { + const TestPartResult& part = part_node->element(); + if (part.failed()) { + const internal::String message = + internal::String::Format("%s:%d\n%s", part.file_name(), + part.line_number(), part.message()); + if (++failures == 1) + fprintf(out, ">\n"); + fprintf(out, + " \n", + EscapeXmlAttribute(message.c_str()).c_str()); + } + } + + if (failures == 0) + fprintf(out, " />\n"); + else + fprintf(out, " \n"); +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, + const TestCase* test_case) { + fprintf(out, + " name()).c_str(), + test_case->total_test_count(), + test_case->failed_test_count(), + test_case->disabled_test_count()); + fprintf(out, + "errors=\"0\" time=\"%s\">\n", + internal::StreamableToString(test_case->elapsed_time()).c_str()); + for (const internal::ListNode* info_node = + test_case->test_info_list().Head(); + info_node != NULL; + info_node = info_node->next()) { + PrintXmlTestInfo(out, test_case->name(), info_node->element()); + } + fprintf(out, " \n"); +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, + const UnitTest* unit_test) { + const internal::UnitTestImpl* const impl = unit_test->impl(); + fprintf(out, "\n"); + fprintf(out, + "total_test_count(), + impl->failed_test_count(), + impl->disabled_test_count(), + internal::StreamableToString(impl->elapsed_time()).c_str()); + fprintf(out, "name=\"AllTests\">\n"); + for (const internal::ListNode* case_node = + impl->test_cases()->Head(); + case_node != NULL; + case_node = case_node->next()) { + PrintXmlTestCase(out, case_node->element()); + } + fprintf(out, "\n"); +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +internal::String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const internal::TestResult* result) { + using internal::TestProperty; + Message attributes; + const internal::List& properties = result->test_properties(); + for (const internal::ListNode* property_node = + properties.Head(); + property_node != NULL; + property_node = property_node->next()) { + const TestProperty& property = property_node->element(); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +namespace internal { + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +// L < UnitTest::mutex_ +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +// L < UnitTest::mutex_ +ScopedTrace::~ScopedTrace() { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as a String. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +// L < mutex_ +// We use "L < mutex_" to denote that the function may acquire mutex_. +String OsStackTraceGetter::CurrentStackTrace(int, int) { + return String(""); +} + +// L < mutex_ +void OsStackTraceGetter::UponLeavingGTest() { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME " internal frames ..."; + +} // namespace internal + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest * UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. +#if _MSC_VER == 1310 && !defined(_DEBUG) // MSVC 7.1 and optimized build. + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // _MSC_VER==1310 && !defined(_DEBUG) +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments()->PushBack(env); + impl_->environments_in_reverse_order()->PushFront(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +// L < mutex_ +void UnitTest::AddTestPartResult(TestPartResultType result_type, + const char* file_name, + int line_number, + const internal::String& message, + const internal::String& os_stack_trace) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack()->size() > 0) { + msg << "\n" << GTEST_NAME << " trace:"; + + for (internal::ListNode* node = + impl_->gtest_trace_stack()->Head(); + node != NULL; + node = node->next()) { + const internal::TraceInfo& trace = node->element(); + msg << "\n" << trace.file << ":" << trace.line << ": " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << "\nStack trace:\n" << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->test_part_result_reporter()->ReportTestPartResult(result); + + // If this is a failure and the user wants the debugger to break on + // failures ... + if (result_type != TPRT_SUCCESS && GTEST_FLAG(break_on_failure)) { + // ... then we generate a seg fault. + *static_cast(NULL) = 1; + } +} + +// Creates and adds a property to the current TestResult. If a property matching +// the supplied value already exists, updates its value instead. +void UnitTest::RecordPropertyForCurrentTest(const char* key, + const char* value) { + const internal::TestProperty test_property(key, value); + impl_->current_test_result()->RecordProperty(test_property); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { +#ifdef GTEST_OS_WINDOWS + +#if !defined(_WIN32_WCE) + // SetErrorMode doesn't exist on CE. + if (GTEST_FLAG(catch_exceptions)) { + // The user wants Google Test to catch exceptions thrown by the tests. + + // This lets fatal errors be handled by us, instead of causing pop-ups. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + } +#endif // _WIN32_WCE + + __try { + return impl_->RunAllTests(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + printf("Exception thrown with code 0x%x.\nFAIL\n", GetExceptionCode()); + fflush(stdout); + return 1; + } + +#else + // We are on Linux or Mac OS. There is no exception of any kind. + + return impl_->RunAllTests(); +#endif // GTEST_OS_WINDOWS +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +// L < mutex_ +const TestCase* UnitTest::current_test_case() const { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +// L < mutex_ +const TestInfo* UnitTest::current_test_info() const { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +// L < mutex_ +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack()->PushFront(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +// L < mutex_ +void UnitTest::PopGTestTrace() { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack()->PopFront(NULL); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), + test_cases_(), + last_death_test_case_(NULL), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + result_printer_(NULL), + os_stack_trace_getter_(NULL), +#ifdef GTEST_HAS_DEATH_TEST + elapsed_time_(0), + internal_run_death_test_flag_(NULL), + death_test_factory_(new DefaultDeathTestFactory) { +#else + elapsed_time_(0) { +#endif // GTEST_HAS_DEATH_TEST + // We do the assignment here instead of in the initializer list, as + // doing that latter causes MSVC to issue a warning about using + // 'this' in initializers. + test_part_result_reporter_ = this; +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + test_cases_.ForEach(internal::Delete); + + // Deletes every Environment. + environments_.ForEach(internal::Delete); + + // Deletes the current test result printer. + delete result_printer_; + + delete os_stack_trace_getter_; +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const String& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + String name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. +// +// Arguments: +// +// test_case_name: name of the test case +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + internal::ListNode* node = test_cases_.FindIf( + TestCaseNameIs(test_case_name)); + + if (node == NULL) { + // No. Let's create one. + TestCase* const test_case = + new TestCase(test_case_name, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (String(test_case_name).EndsWith("DeathTest")) { + // Yes. Inserts the test case after the last death test case + // defined so far. + node = test_cases_.InsertAfter(last_death_test_case_, test_case); + last_death_test_case_ = node; + } else { + // No. Appends to the end of the list. + test_cases_.PushBack(test_case); + node = test_cases_.Last(); + } + } + + // Returns the TestCase found. + return node->element(); +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the List::ForEach() method. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns 0 if all tests are successful, or 1 otherwise. If any +// exception is thrown during a test on Windows, this test is +// considered to be failed, but the rest of the tests will still be +// run. (We disable exceptions on Linux and Mac OS X, so the issue +// doesn't apply there.) +int UnitTestImpl::RunAllTests() { + // True iff Google Test is initialized before RUN_ALL_TESTS() is called. + const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized(); + + // Lists all the tests and exits if the --gtest_list_tests + // flag was specified. + if (GTEST_FLAG(list_tests)) { + ListAllTests(); + return 0; + } + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#ifdef GTEST_HAS_DEATH_TEST + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + UnitTestEventListenerInterface * const printer = result_printer(); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests() > 0; + // True iff at least one test has failed. + bool failed = false; + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + if (repeat != 1) { + printf("\nRepeating all tests (iteration %d) . . .\n\n", i + 1); + } + + // Tells the unit test event listener that the tests are about to + // start. + printer->OnUnitTestStart(parent_); + + const TimeInMillis start = GetTimeInMillis(); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + printer->OnGlobalSetUpStart(parent_); + environments_.ForEach(SetUpEnvironment); + printer->OnGlobalSetUpEnd(parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + test_cases_.ForEach(TestCase::RunTestCase); + } + + // Tears down all environments in reverse order afterwards. + printer->OnGlobalTearDownStart(parent_); + environments_in_reverse_order_.ForEach(TearDownEnvironment); + printer->OnGlobalTearDownEnd(parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just + // finished. + printer->OnUnitTestEnd(parent_); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + ClearResult(); + } + + if (!gtest_is_initialized_before_run_all_tests) { + ColoredPrintf( + COLOR_RED, "\nIMPORTANT NOTICE - DO NOT IGNORE:\n" + "This test program did NOT call %s() before calling RUN_ALL_TESTS(). " + "This is INVALID. Soon " GTEST_NAME + " will start to enforce the valid usage. " + "Please fix it ASAP, or IT WILL START TO FAIL.\n", + "testing::ParseGTestFlags" + ); // NOLINT + } + + // Returns 0 if all tests passed, or 1 other wise. + return failed ? 1 : 0; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests() { + int num_runnable_tests = 0; + for (const internal::ListNode *test_case_node = + test_cases_.Head(); + test_case_node != NULL; + test_case_node = test_case_node->next()) { + TestCase * const test_case = test_case_node->element(); + const String &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (const internal::ListNode *test_info_node = + test_case->test_info_list().Head(); + test_info_node != NULL; + test_info_node = test_info_node->next()) { + TestInfo * const test_info = test_info_node->element(); + const String test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestPattern. + const bool is_disabled = + internal::UnitTestOptions::PatternMatchesString(kDisableTestPattern, + test_case_name.c_str()) || + internal::UnitTestOptions::PatternMatchesString(kDisableTestPattern, + test_name.c_str()); + test_info->impl()->set_is_disabled(is_disabled); + + const bool should_run = !is_disabled && + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->impl()->set_should_run(should_run); + test_case->set_should_run(test_case->should_run() || should_run); + if (should_run) { + num_runnable_tests++; + } + } + } + return num_runnable_tests; +} + +// Lists all tests by name. +void UnitTestImpl::ListAllTests() { + for (const internal::ListNode* test_case_node = test_cases_.Head(); + test_case_node != NULL; + test_case_node = test_case_node->next()) { + const TestCase* const test_case = test_case_node->element(); + + // Prints the test case name following by an indented list of test nodes. + printf("%s.\n", test_case->name()); + + for (const internal::ListNode* test_info_node = + test_case->test_info_list().Head(); + test_info_node != NULL; + test_info_node = test_info_node->next()) { + const TestInfo* const test_info = test_info_node->element(); + + printf(" %s\n", test_info->name()); + } + } + fflush(stdout); +} + +// Sets the unit test result printer. +// +// Does nothing if the input and the current printer object are the +// same; otherwise, deletes the old printer object and makes the +// input the current printer. +void UnitTestImpl::set_result_printer( + UnitTestEventListenerInterface* result_printer) { + if (result_printer_ != result_printer) { + delete result_printer_; + result_printer_ = result_printer; + } +} + +// Returns the current unit test result printer if it is not NULL; +// otherwise, creates an appropriate result printer, makes it the +// current printer, and returns it. +UnitTestEventListenerInterface* UnitTestImpl::result_printer() { + if (result_printer_ != NULL) { + return result_printer_; + } + +#ifdef GTEST_HAS_DEATH_TEST + if (internal_run_death_test_flag_.get() != NULL) { + result_printer_ = new NullUnitTestResultPrinter; + return result_printer_; + } +#endif // GTEST_HAS_DEATH_TEST + + UnitTestEventsRepeater *repeater = new UnitTestEventsRepeater; + const String& output_format = internal::UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + repeater->AddListener(new XmlUnitTestResultPrinter( + internal::UnitTestOptions::GetOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } + repeater->AddListener(new PrettyUnitTestResultPrinter); + result_printer_ = repeater; + return result_printer_; +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +internal::TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + current_test_info_->impl()->result() : &ad_hoc_test_result_; +} + +// TestInfoImpl constructor. +TestInfoImpl::TestInfoImpl(TestInfo* parent, + const char* test_case_name, + const char* name, + TypeId fixture_class_id, + TestMaker maker) : + parent_(parent), + test_case_name_(String(test_case_name)), + name_(String(name)), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + maker_(maker) { +} + +// TestInfoImpl destructor. +TestInfoImpl::~TestInfoImpl() { +} + +} // namespace internal + +namespace internal { + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX. + const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX, flag); + const size_t flag_len = flag_str.GetLength(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// The internal implementation of ParseGTestFlags(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void ParseGTestFlagsImpl(int* argc, CharType** argv) { + g_parse_gtest_flags_called = true; + if (*argc <= 0) return; + +#ifdef GTEST_HAS_DEATH_TEST + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } +#endif // GTEST_HAS_DEATH_TEST + + for (int i = 1; i != *argc; i++) { + const String arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } +} + +} // namespace internal + +// Parses a command line for the flags that Google Test recognizes. +// Whenever a Google Test flag is seen, it is removed from argv, and *argc +// is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +void ParseGTestFlags(int* argc, char** argv) { + internal::g_executable_path = argv[0]; + internal::ParseGTestFlagsImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +#ifdef GTEST_OS_WINDOWS +void ParseGTestFlags(int* argc, wchar_t** argv) { + // g_executable_path uses normal characters rather than wide chars, so call + // StreamableToString to convert argv[0] to normal characters (utf8 encoding). + internal::g_executable_path = internal::StreamableToString(argv[0]); + internal::ParseGTestFlagsImpl(argc, argv); +} +#endif // GTEST_OS_WINDOWS + +} // namespace testing diff --git a/src/gtest/gtest.h b/src/gtest/gtest.h new file mode 100644 index 00000000..4e3d7bcb --- /dev/null +++ b/src/gtest/gtest.h @@ -0,0 +1,1243 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#ifndef GTEST_NOT_MAC_FRAMEWORK_MODE +// Protobuf never uses gTest in "mac framework mode". +#define GTEST_NOT_MAC_FRAMEWORK_MODE +#endif + +// The following platform macros are used throughout Google Test: +// _WIN32_WCE Windows CE (set in project files) +// __SYMBIAN32__ Symbian (set by Symbian tool chain) +// +// Note that even though _MSC_VER and _WIN32_WCE really indicate a compiler +// and a Win32 implementation, respectively, we use them to indicate the +// combination of compiler - Win 32 API - C library, since the code currently +// only supports: +// Windows proper with Visual C++ and MS C library (_MSC_VER && !_WIN32_WCE) and +// Windows Mobile with Visual C++ and no C library (_WIN32_WCE). + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes +// will be in the framework headers folder along with gtest.h. Define +// GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on the +// Mac and are not using it as a framework. More info on frameworks +// available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-death-test.h" // NOLINT +#include "gtest-internal.h" // NOLINT +#include "gtest-message.h" // NOLINT +#include "gtest-string.h" // NOLINT +#include "gtest_prod.h" // NOLINT +#else +#include +#include +#include +#include +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +// Depending on the platform, different string classes are available. +// On Windows, ::std::string compiles only when exceptions are +// enabled. On Linux, in addition to ::std::string, Google also makes +// use of class ::string, which has the same interface as +// ::std::string, but has a different implementation. +// +// The user can tell us whether ::std::string is available in his +// environment by defining the macro GTEST_HAS_STD_STRING to either 1 +// or 0 on the compiler command line. He can also define +// GTEST_HAS_GLOBAL_STRING to 1 to indicate that ::string is available +// AND is a distinct type to ::std::string, or define it to 0 to +// indicate otherwise. +// +// If the user's ::std::string and ::string are the same class due to +// aliasing, he should define GTEST_HAS_STD_STRING to 1 and +// GTEST_HAS_GLOBAL_STRING to 0. +// +// If the user doesn't define GTEST_HAS_STD_STRING and/or +// GTEST_HAS_GLOBAL_STRING, they are defined heuristically. + +namespace testing { + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32(stack_trace_depth); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool(show_internal_stack_frames); + +// The possible outcomes of a test part (i.e. an assertion or an +// explicit SUCCEED(), FAIL(), or ADD_FAILURE()). +enum TestPartResultType { + TPRT_SUCCESS, // Succeeded. + TPRT_NONFATAL_FAILURE, // Failed but the test can continue. + TPRT_FATAL_FAILURE // Failed and the test should be terminated. +}; + +namespace internal { + +class GTestFlagSaver; + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +// Declared in gtest-internal.h but defined here, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template +String StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that described how it failed. +// +// This class is useful for defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// The constructor of AssertionResult is private. To create an +// instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// For example, in order to be able to write: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you just need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) return testing::AssertionSuccess(); +// +// Message msg; +// msg << "Expected: " << expr << " is even\n" +// << " Actual: it's " << n; +// return testing::AssertionFailure(msg); +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +class AssertionResult { + public: + // Declares factory functions for making successful and failed + // assertion results as friends. + friend AssertionResult AssertionSuccess(); + friend AssertionResult AssertionFailure(const Message&); + + // Returns true iff the assertion succeeded. + operator bool() const { return failure_message_.c_str() == NULL; } // NOLINT + + // Returns the assertion's failure message. + const char* failure_message() const { return failure_message_.c_str(); } + + private: + // The default constructor. It is used when the assertion succeeded. + AssertionResult() {} + + // The constructor used when the assertion failed. + explicit AssertionResult(const internal::String& failure_message); + + // Stores the assertion's failure message. + internal::String failure_message_; +}; + +// Makes a successful assertion result. +AssertionResult AssertionSuccess(); + +// Makes a failed assertion result with the given failure message. +AssertionResult AssertionFailure(const Message& msg); + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { ... } +// virtual void TearDown() { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class Test { + public: + friend class internal::TestInfoImpl; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef void (*SetUpTestCaseFunc)(); + typedef void (*TearDownTestCaseFunc)(); + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Logs a property for the current test. Only the last value for a given + // key is remembered. + // These are public static so they can be called from utility functions + // that are not members of the test fixture. + // The arguments are const char* instead strings, as Google Test is used + // on platforms where string doesn't compile. + // + // Note that a driving consideration for these RecordProperty methods + // was to produce xml output suited to the Greenspan charting utility, + // which at present will only chart values that fit in a 32-bit int. It + // is the user's responsibility to restrict their values to 32-bit ints + // if they intend them to be used with Greenspan. + static void RecordProperty(const char* key, const char* value); + static void RecordProperty(const char* key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Uses a GTestFlagSaver to save and restore all Google Test flags. + const internal::GTestFlagSaver* const gtest_flag_saver_; + + // Often a user mis-spells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if a user declares void Setup() in his test + // fixture. + // + // - This method is private, so it will be another compiler error + // if a user calls it from his test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN(Test); +}; + + +// Defines the type of a function pointer that creates a Test object +// when invoked. +typedef Test* (*TestMaker)(); + + +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Creates a TestInfo object and registers it with the UnitTest + // singleton; returns the created object. + // + // Arguments: + // + // test_case_name: name of the test case + // name: name of the test + // fixture_class_id: ID of the test fixture class + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // maker: pointer to the function that creates a test object + // + // This is public only because it's needed by the TEST and TEST_F macros. + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + static TestInfo* MakeAndRegisterInstance( + const char* test_case_name, + const char* name, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestMaker maker); + + // Returns the test case name. + const char* test_case_name() const; + + // Returns the test name. + const char* name() const; + + // Returns true if this test should run. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const; + + // Returns the result of the test. + const internal::TestResult* result() const; + private: +#ifdef GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif + friend class internal::TestInfoImpl; + friend class internal::UnitTestImpl; + friend class Test; + friend class TestCase; + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count(); + + // Accessors for the implementation object. + internal::TestInfoImpl* impl() { return impl_; } + const internal::TestInfoImpl* impl() const { return impl_; } + + // Constructs a TestInfo object. + TestInfo(const char* test_case_name, const char* name, + internal::TypeId fixture_class_id, TestMaker maker); + + // An opaque implementation object. + internal::TestInfoImpl* impl_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(TestInfo); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. The user should subclass this to define his own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +// A UnitTest consists of a list of TestCases. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + void AddTestPartResult(TestPartResultType result_type, + const char* file_name, + int line_number, + const internal::String& message, + const internal::String& os_stack_trace); + + // Adds a TestProperty to the current TestResult object. If the result already + // contains a property with the same key, the value will be updated. + void RecordPropertyForCurrentTest(const char* key, const char* value); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT; + + // Returns the TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const; + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const; + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + private: + // ScopedTrace is a friend as it needs to modify the per-thread + // trace stack, which is a private member of UnitTest. + friend class internal::ScopedTrace; + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace(); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Parses a command line for the flags that Google Test recognizes. +// Whenever a Google Test flag is seen, it is removed from argv, and *argc +// is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +void ParseGTestFlags(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +#ifdef GTEST_OS_WINDOWS +void ParseGTestFlags(int* argc, wchar_t** argv); +#endif // GTEST_OS_WINDOWS + +namespace internal { + +// These overloaded versions handle ::std::string and ::std::wstring. +#if GTEST_HAS_STD_STRING +inline String FormatForFailureMessage(const ::std::string& str) { + return (Message() << '"' << str << '"').GetString(); +} +#endif // GTEST_HAS_STD_STRING +#if GTEST_HAS_STD_WSTRING +inline String FormatForFailureMessage(const ::std::wstring& wstr) { + return (Message() << "L\"" << wstr << '"').GetString(); +} +#endif // GTEST_HAS_STD_WSTRING + +// These overloaded versions handle ::string and ::wstring. +#if GTEST_HAS_GLOBAL_STRING +inline String FormatForFailureMessage(const ::string& str) { + return (Message() << '"' << str << '"').GetString(); +} +#endif // GTEST_HAS_GLOBAL_STRING +#if GTEST_HAS_GLOBAL_WSTRING +inline String FormatForFailureMessage(const ::wstring& wstr) { + return (Message() << "L\"" << wstr << '"').GetString(); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char*, and print it as a C string when it is compared against an +// std::string object, for example. +// +// The default implementation ignores the type of the other operand. +// Some specialized versions are used to handle formatting wide or +// narrow C strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +String FormatForComparisonFailureMessage(const T1& value, + const T2& /* other_operand */) { + return FormatForFailureMessage(value); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// With this overloaded version, we allow anonymous enums to be used +// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums +// can be implicitly cast to BiggestInt. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template +class EqHelper { + public: + // This templatized version is for the general case. + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal. +template <> +class EqHelper { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // This version will be picked when the second argument to + // ASSERT_EQ() is a pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + T2* actual) { + // We already know that 'expected' is a null pointer. + return CmpHelperEQ(expected_expression, actual_expression, + static_cast(NULL), actual); + } +}; + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +#define GTEST_IMPL_CMP_HELPER(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + Message msg;\ + msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + return AssertionFailure(msg);\ + }\ +}\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +#if GTEST_HAS_STD_STRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +#endif // GTEST_HAS_STD_STRING +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, + const char* actual_expression, + RawType expected, + RawType actual) { + const FloatingPoint lhs(expected), rhs(actual); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + StrStream expected_ss; + expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << expected; + + StrStream actual_ss; + actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << actual; + + return EqFailure(expected_expression, + actual_expression, + StrStreamToString(&expected_ss), + StrStreamToString(&actual_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResultType type, const char* file, int line, + const char* message); + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE macro below. + void operator=(const Message& message) const; + private: + TestPartResultType const type_; + const char* const file_; + int const line_; + String const message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(AssertHelper); +}; + +} // namespace internal + +// Macros for indicating success/failure in test code. + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. +// +// Examples: +// +// EXPECT_TRUE(server.StatusIsOK()); +// ASSERT_FALSE(server.HasPendingRequest(port)) +// << "There are still pending requests " << "on port " << port; + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE("Failed") + +// Generates a fatal failure with a generic message. +#define FAIL() GTEST_FATAL_FAILURE("Failed") + +// Generates a success with a generic message. +#define SUCCEED() GTEST_SUCCESS("Succeeded") + +// Boolean assertions. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE) + +// Includes the auto-generated header that implements a family of +// generic predicate assertion macros. +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest_pred_impl.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to +// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(5, Foo()); +// EXPECT_EQ(NULL, a_pointer); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define EXPECT_NE(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define ASSERT_EQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// C String Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_DOUBLE_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_FLOAT_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_DOUBLE_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#ifdef GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the expected result +// and the actual result with both a human-readable string representation of +// the error, if available, as well as the hex result code. +#define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +#define SCOPED_TRACE(message) \ + ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, ::testing::Message() << (message)) + + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// The user should put his test code between braces after using this +// macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +#define TEST(test_case_name, test_name)\ + GTEST_TEST(test_case_name, test_name, ::testing::Test) + + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// his test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(0, a_.size()); +// EXPECT_EQ(1, b_.size()); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST(test_fixture, test_name, test_fixture) + +// Use this macro in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by ParseGTestFlags(). + +#define RUN_ALL_TESTS()\ + (::testing::UnitTest::GetInstance()->Run()) + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/src/gtest/gtest_main.cc b/src/gtest/gtest_main.cc new file mode 100644 index 00000000..c216bd2d --- /dev/null +++ b/src/gtest/gtest_main.cc @@ -0,0 +1,39 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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 + +#include + +int main(int argc, char **argv) { + std::cout << "Running main() from gtest_main.cc\n"; + + testing::ParseGTestFlags(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/gtest/gtest_pred_impl.h b/src/gtest/gtest_pred_impl.h new file mode 100644 index 00000000..984f7930 --- /dev/null +++ b/src/gtest/gtest_pred_impl.h @@ -0,0 +1,368 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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 is AUTOMATICALLY GENERATED on 06/22/2008 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1(pred_format, v1, on_failure)\ + GTEST_ASSERT(pred_format(#v1, v1),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1(pred, v1, on_failure)\ + GTEST_ASSERT(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1(pred_format, v1, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1(pred, v1, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1(pred_format, v1, GTEST_FATAL_FAILURE) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1(pred, v1, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, v1, v2),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2(pred, v1, v2, on_failure)\ + GTEST_ASSERT(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2(pred_format, v1, v2, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2(pred, v1, v2, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2(pred_format, v1, v2, GTEST_FATAL_FAILURE) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2(pred, v1, v2, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, #v3, v1, v2, v3),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3(pred, v1, v2, v3, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE) + + + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/src/gtest/gtest_prod.h b/src/gtest/gtest_prod.h new file mode 100644 index 00000000..da80ddc6 --- /dev/null +++ b/src/gtest/gtest_prod.h @@ -0,0 +1,58 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST(MyClassTest, MyMethod); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, MyMethod) { +// // Can call MyClass::MyMethod() here. +// } + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/src/gtest/internal/gtest-death-test-internal.h b/src/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 00000000..b49c6e47 --- /dev/null +++ b/src/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,201 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#ifdef GTEST_HAS_DEATH_TEST + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the two reasons that a test might be aborted. + enum AbortReason { TEST_ENCOUNTERED_RETURN_STATEMENT, TEST_DID_NOT_DIE }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN(DeathTest); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status); + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST(statement, predicate, regex, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER \ + if (true) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ + gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel \ + gtest_sentinel(gtest_dt); \ + { statement; } \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN(gtest_label_, __LINE__): \ + fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// A struct representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +struct InternalRunDeathTestFlag { + String file; + int line; + int index; + int status_fd; +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/src/gtest/internal/gtest-filepath.h b/src/gtest/internal/gtest-filepath.h new file mode 100644 index 00000000..6f63718d --- /dev/null +++ b/src/gtest/internal/gtest-filepath.h @@ -0,0 +1,168 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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: keith.ray@gmail.com (Keith Ray) +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in testing/base/internal/gtest-internal.h +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-string.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + explicit FilePath(const char* pathname) : pathname_(pathname) { } + explicit FilePath(const String& pathname) : pathname_(pathname) { } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + String ToString() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + private: + String pathname_; + + // Don't implement operator= because it is banned by the style guide. + FilePath& operator=(const FilePath& rhs); +}; // class FilePath + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/src/gtest/internal/gtest-internal.h b/src/gtest/internal/gtest-internal.h new file mode 100644 index 00000000..2be1b4ac --- /dev/null +++ b/src/gtest/internal/gtest-internal.h @@ -0,0 +1,569 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-port.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +#ifdef GTEST_OS_LINUX +#include +#include +#include +#include +#endif // GTEST_OS_LINUX + +#include // NOLINT +#include // NOLINT + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-string.h" // NOLINT +#include "gtest-filepath.h" // NOLINT +#else +#include +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN(foo, bar) GTEST_CONCAT_TOKEN_IMPL(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL(foo, bar) foo ## bar + +// Google Test defines the testing::Message class to allow construction of +// test messages via the << operator. The idea is that anything +// streamable to std::ostream can be streamed to a testing::Message. +// This allows a user to use his own types in Google Test assertions by +// overloading the << operator. +// +// util/gtl/stl_logging-inl.h overloads << for STL containers. These +// overloads cannot be defined in the std namespace, as that will be +// undefined behavior. Therefore, they are defined in the global +// namespace instead. +// +// C++'s symbol lookup rule (i.e. Koenig lookup) says that these +// overloads are visible in either the std namespace or the global +// namespace, but not other namespaces, including the testing +// namespace which Google Test's Message class is in. +// +// To allow STL containers (and other types that has a << operator +// defined in the global namespace) to be used in Google Test assertions, +// testing::Message must access the custom << operator from the global +// namespace. Hence this helper function. +// +// Note: Jeffrey Yasskin suggested an alternative fix by "using +// ::operator<<;" in the definition of Message's operator<<. That fix +// doesn't require a helper function, but unfortunately doesn't +// compile with MSVC. +template +inline void GTestStreamToHelper(std::ostream* os, const T& val) { + *os << val; +} + +namespace testing { + +// Forward declaration of classes. + +class Message; // Represents a failure message. +class TestCase; // A collection of related tests. +class TestPartResult; // Result of a test part. +class TestInfo; // Information about a test. +class UnitTest; // A collection of test cases. +class UnitTestEventListenerInterface; // Listens to Google Test events. +class AssertionResult; // Result of an assertion. + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class ScopedTrace; // Implements scoped trace. +class TestInfoImpl; // Opaque implementation of TestInfo +class TestResult; // Result of a single Test. +class UnitTestImpl; // Opaque implementation of UnitTest + +template class List; // A generic list. +template class ListNode; // A node in a generic list. + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef __SYMBIAN32__ // Symbian +// Passing non-POD classes through ellipsis (...) crashes the ARM compiler. +// The Nokia Symbian compiler tries to instantiate a copy constructor for +// objects passed through ellipsis (...), failing for uncopyable objects. +// Hence we define this to false (and lose support for NULL detection). +#define GTEST_IS_NULL_LITERAL(x) false +#else // ! __SYMBIAN32__ +#define GTEST_IS_NULL_LITERAL(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // __SYMBIAN32__ + +// Appends the user-supplied message to the Google-Test-generated message. +String AppendUserMessage(const String& gtest_msg, + const Message& user_msg); + +// A helper class for creating scoped traces in user programs. +class ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + ScopedTrace(const char* file, int line, const Message& message); + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +// Declared here but defined in gtest.h, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template +String StreamableToString(const T& streamable); + +// Formats a value to be used in a failure message. + +#ifdef __SYMBIAN32__ + +// These are needed as the Nokia Symbian Compiler cannot decide between +// const T& and const T* in a function template. The Nokia compiler _can_ +// decide between class template specializations for T and T*, so a +// tr1::type_traits-like is_pointer works, and we can overload on that. + +// This overload makes sure that all pointers (including +// those to char or wchar_t) are printed as raw pointers. +template +inline String FormatValueForFailureMessage(internal::true_type dummy, + T* pointer) { + return StreamableToString(static_cast(pointer)); +} + +template +inline String FormatValueForFailureMessage(internal::false_type dummy, + const T& value) { + return StreamableToString(value); +} + +template +inline String FormatForFailureMessage(const T& value) { + return FormatValueForFailureMessage( + typename internal::is_pointer::type(), value); +} + +#else + +template +inline String FormatForFailureMessage(const T& value) { + return StreamableToString(value); +} + +// This overload makes sure that all pointers (including +// those to char or wchar_t) are printed as raw pointers. +template +inline String FormatForFailureMessage(T* pointer) { + return StreamableToString(static_cast(pointer)); +} + +#endif // __SYMBIAN32__ + +// These overloaded versions handle narrow and wide characters. +String FormatForFailureMessage(char ch); +String FormatForFailureMessage(wchar_t wchar); + +// When this operand is a const char* or char*, and the other operand +// is a ::std::string or ::string, we print this operand as a C string +// rather than a pointer. We do the same for wide strings. + +// This internal macro is used to avoid duplicated code. +#define GTEST_FORMAT_IMPL(operand2_type, operand1_printer)\ +inline String FormatForComparisonFailureMessage(\ + operand2_type::value_type* str, const operand2_type& operand2) {\ + return operand1_printer(str);\ +}\ +inline String FormatForComparisonFailureMessage(\ + const operand2_type::value_type* str, const operand2_type& operand2) {\ + return operand1_printer(str);\ +} + +#if GTEST_HAS_STD_STRING +GTEST_FORMAT_IMPL(::std::string, String::ShowCStringQuoted) +#endif // GTEST_HAS_STD_STRING +#if GTEST_HAS_STD_WSTRING +GTEST_FORMAT_IMPL(::std::wstring, String::ShowWideCStringQuoted) +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_STRING +GTEST_FORMAT_IMPL(::string, String::ShowCStringQuoted) +#endif // GTEST_HAS_GLOBAL_STRING +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_FORMAT_IMPL(::wstring, String::ShowWideCStringQuoted) +#endif // GTEST_HAS_GLOBAL_WSTRING + +#undef GTEST_FORMAT_IMPL + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const String& expected_value, + const String& actual_value, + bool ignoring_case); + + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. + static const size_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) : value_(x) {} + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.bits_ = bits; + return fp.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(bits_, rhs.bits_) <= kMaxUlps; + } + + private: + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + union { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; +}; + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test case, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef void* TypeId; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +inline TypeId GetTypeId() { + static bool dummy = false; + // The compiler is required to create an instance of the static + // variable dummy for each T used to instantiate the template. + // Therefore, the address of dummy is guaranteed to be unique. + return &dummy; +} + +#ifdef GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +AssertionResult IsHRESULTSuccess(const char* expr, long hr); // NOLINT +AssertionResult IsHRESULTFailure(const char* expr, long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +} // namespace internal +} // namespace testing + +#define GTEST_MESSAGE(message, result_type) \ + ::testing::internal::AssertHelper(result_type, __FILE__, __LINE__, message) \ + = ::testing::Message() + +#define GTEST_FATAL_FAILURE(message) \ + return GTEST_MESSAGE(message, ::testing::TPRT_FATAL_FAILURE) + +#define GTEST_NONFATAL_FAILURE(message) \ + GTEST_MESSAGE(message, ::testing::TPRT_NONFATAL_FAILURE) + +#define GTEST_SUCCESS(message) \ + GTEST_MESSAGE(message, ::testing::TPRT_SUCCESS) + +#define GTEST_TEST_BOOLEAN(boolexpr, booltext, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER \ + if (boolexpr) \ + ; \ + else \ + fail("Value of: " booltext "\n Actual: " #actual "\nExpected: " #expected) + +// Helper macro for defining tests. +#define GTEST_TEST(test_case_name, test_name, parent_class)\ +class test_case_name##_##test_name##_Test : public parent_class {\ + public:\ + test_case_name##_##test_name##_Test() {}\ + static ::testing::Test* NewTest() {\ + return new test_case_name##_##test_name##_Test;\ + }\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN(test_case_name##_##test_name##_Test);\ +};\ +\ +::testing::TestInfo* const test_case_name##_##test_name##_Test::test_info_ =\ + ::testing::TestInfo::MakeAndRegisterInstance(\ + #test_case_name, \ + #test_name, \ + ::testing::internal::GetTypeId< parent_class >(), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + test_case_name##_##test_name##_Test::NewTest);\ +void test_case_name##_##test_name##_Test::TestBody() + + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/src/gtest/internal/gtest-port.h b/src/gtest/internal/gtest-port.h new file mode 100644 index 00000000..36d5a149 --- /dev/null +++ b/src/gtest/internal/gtest-port.h @@ -0,0 +1,596 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +#ifndef GTEST_NOT_MAC_FRAMEWORK_MODE +// Protobuf never uses gTest in "mac framework mode". +#define GTEST_NOT_MAC_FRAMEWORK_MODE +#endif + +// The user can define the following macros in the build script to +// control Google Test's behavior: +// +// GTEST_HAS_STD_STRING - Define it to 1/0 to indicate that +// std::string does/doesn't work (Google Test can be +// used where std::string is unavailable). Leave +// it undefined to let Google Test define it. +// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define ::string, +// which is different to std::string). Leave it +// undefined to let Google Test define it. + +// This header defines the following utilities: +// +// Macros indicating the name of the Google C++ Testing Framework project: +// GTEST_NAME - a string literal of the project name. +// GTEST_FLAG_PREFIX - a string literal of the prefix all Google +// Test flag names share. +// GTEST_FLAG_PREFIX_UPPER - a string literal of the prefix all Google +// Test flag names share, in upper case. +// +// Macros indicating the current platform: +// GTEST_OS_LINUX - defined iff compiled on Linux. +// GTEST_OS_MAC - defined iff compiled on Mac OS X. +// GTEST_OS_WINDOWS - defined iff compiled on Windows. +// Note that it is possible that none of the GTEST_OS_ macros are defined. +// +// Macros indicating available Google Test features: +// GTEST_HAS_DEATH_TEST - defined iff death tests are supported. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED - declares that a class' instances don't have to +// be used. +// GTEST_DISALLOW_COPY_AND_ASSIGN() - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT - declares that a function's result must be used. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian only. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax. Not available on +// Windows. +// +// Logging: +// GTEST_LOG() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stderr capturing: +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_FLAG() - references a flag. +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an Int32 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include + +#include +#include + +#define GTEST_NAME "Google Test" +#define GTEST_FLAG_PREFIX "gtest_" +#define GTEST_FLAG_PREFIX_UPPER "GTEST_" + +// Determines the platform on which Google Test is compiled. +#ifdef _MSC_VER +// TODO(kenton): GTEST_OS_WINDOWS is currently used to mean both "The OS is +// Windows" and "The compiler is MSVC". These meanings really should be +// separated in order to better support Windows compilers other than MSVC. +// Then again, the macro _WIN32 is already a good way to check for the first +// case and _MSC_VER is a good way to check for the latter, so maybe +// GTEST_OS_WINDOWS should be removed? +#define GTEST_OS_WINDOWS +#elif defined __APPLE__ +#define GTEST_OS_MAC +#elif defined __linux__ +#define GTEST_OS_LINUX +#endif // _MSC_VER + +// Determines whether ::std::string and ::string are available. + +#ifndef GTEST_HAS_STD_STRING +// The user didn't tell us whether ::std::string is available, so we +// need to figure it out. + +#ifdef GTEST_OS_WINDOWS +// Assumes that exceptions are enabled by default. +#ifndef _HAS_EXCEPTIONS +#define _HAS_EXCEPTIONS 1 +#endif // _HAS_EXCEPTIONS +// GTEST_HAS_EXCEPTIONS is non-zero iff exceptions are enabled. It is +// always defined, while _HAS_EXCEPTIONS is defined only on Windows. +#define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +// On Windows, we can use ::std::string if the compiler version is VS +// 2005 or above, or if exceptions are enabled. +#define GTEST_HAS_STD_STRING ((_MSC_VER >= 1400) || GTEST_HAS_EXCEPTIONS) +#else // We are on Linux or Mac OS. +#define GTEST_HAS_EXCEPTIONS 0 +#define GTEST_HAS_STD_STRING 1 +#endif // GTEST_OS_WINDOWS + +#endif // GTEST_HAS_STD_STRING + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +#define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_STD_STRING || GTEST_HAS_GLOBAL_STRING +#include // NOLINT +#endif // GTEST_HAS_STD_STRING || GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_STD_STRING +#include // NOLINT +#else +#include // NOLINT +#endif // GTEST_HAS_STD_STRING + +// Determines whether to support death tests. +#if GTEST_HAS_STD_STRING && defined(GTEST_OS_LINUX) +#define GTEST_HAS_DEATH_TEST +// On some platforms, needs someone to define size_t, and +// won't compile if being #included first. Therefore it's important +// that we #include it after . +#include +#include +#include +#include +#endif // GTEST_HAS_STD_STRING && defined(GTEST_OS_LINUX) + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +#define GTEST_AMBIGUOUS_ELSE_BLOCKER +#else +#define GTEST_AMBIGUOUS_ELSE_BLOCKER switch (0) case 0: // NOLINT +#endif + +// Use this annotation at the end of a struct / class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED; +#if defined(GTEST_OS_WINDOWS) || (defined(GTEST_OS_LINUX) && defined(SWIG)) +#define GTEST_ATTRIBUTE_UNUSED +#else +#define GTEST_ATTRIBUTE_UNUSED __attribute__ ((unused)) +#endif // GTEST_OS_WINDOWS || (GTEST_OS_LINUX && SWIG) + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN(type)\ + type(const type &);\ + void operator=(const type &) + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT; +#if defined(__GNUC__) \ + && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \ + && !defined(COMPILER_ICC) +#define GTEST_MUST_USE_RESULT __attribute__ ((warn_unused_result)) +#else +#define GTEST_MUST_USE_RESULT +#endif // (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 4) + +namespace testing { + +class Message; + +namespace internal { + +class String; + +// std::strstream is deprecated. However, we have to use it on +// Windows as std::stringstream won't compile on Windows when +// exceptions are disabled. We use std::stringstream on other +// platforms to avoid compiler warnings there. +#if GTEST_HAS_STD_STRING +typedef ::std::stringstream StrStream; +#else +typedef ::std::strstream StrStream; +#endif // GTEST_HAS_STD_STRING + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template +class scoped_ptr { + public: + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (sizeof(T) > 0) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(scoped_ptr); +}; + +#ifdef GTEST_HAS_DEATH_TEST + +// Defines RE. Currently only needed for death tests. + +// A simple C++ wrapper for . It uses the POSIX Enxtended +// Regular Expression syntax. +class RE { + public: + // Constructs an RE from a string. +#if GTEST_HAS_STD_STRING + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT +#endif // GTEST_HAS_STD_STRING + +#if GTEST_HAS_GLOBAL_STRING + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT +#endif // GTEST_HAS_GLOBAL_STRING + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // Returns true iff str contains regular expression re. + + // TODO(wan): make PartialMatch() work when str contains NUL + // characters. +#if GTEST_HAS_STD_STRING + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } +#endif // GTEST_HAS_STD_STRING + +#if GTEST_HAS_GLOBAL_STRING + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } +#endif // GTEST_HAS_GLOBAL_STRING + + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of a string, as Google Test may be used + // where string is not available. We also do not use Google Test's own + // String type here, in order to simplify dependencies between the + // files. + const char* pattern_; + regex_t regex_; + bool is_valid_; +}; + +#endif // GTEST_HAS_DEATH_TEST + +// Defines logging utilities: +// GTEST_LOG() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +void GTestLog(GTestLogSeverity severity, const char* file, + int line, const char* msg); + +#define GTEST_LOG(severity, msg)\ + ::testing::internal::GTestLog(\ + ::testing::internal::GTEST_##severity, __FILE__, __LINE__, \ + (::testing::Message() << (msg)).GetString().c_str()) + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +// Defines the stderr capturer: +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. + +#ifdef GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by ParseGTestFlags(). +extern ::std::vector g_argvs; + +void CaptureStderr(); +// GTEST_HAS_DEATH_TEST implies we have ::std::string. +::std::string GetCapturedStderr(); +const ::std::vector& GetArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + explicit Mutex(int unused) {} + void AssertHeld() const {} + enum { NO_CONSTRUCTOR_NEEDED_FOR_STATIC_MUTEX = 0 }; +}; + +// We cannot call it MutexLock directly as the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class ThreadLocal { + public: + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +// There's no portable way to detect the number of threads, so we just +// return 0 to indicate that we cannot detect it. +// CHANGED FOR PROTOBUF: The protobuf tests do not use multiple threads, +// so we know there is one thread. +inline size_t GetThreadCount() { return 1; } + +// Defines tr1::is_pointer (only needed for Symbian). + +#ifdef __SYMBIAN32__ + +// Symbian does not have tr1::type_traits, so we define our own is_pointer +// These are needed as the Nokia Symbian Compiler cannot decide between +// const T& and const T* in a function template. + +template +struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; +}; +template const bool bool_constant::value; + +typedef bool_constant false_type; +typedef bool_constant true_type; + +template +struct is_pointer : public false_type {}; + +template +struct is_pointer : public true_type {}; + +#endif // __SYMBIAN32__ + +// Defines BiggestInt as the biggest signed integer type the compiler +// supports. + +#ifdef GTEST_OS_WINDOWS +typedef __int64 BiggestInt; +#else +typedef long long BiggestInt; // NOLINT +#endif // GTEST_OS_WINDOWS + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: +#ifdef GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// A wrapper for getenv() that works on Linux, Windows, and Mac OS. +inline const char* GetEnv(const char* name) { +#ifdef _WIN32_WCE // We are on Windows CE. + // CE has no environment variables. + return NULL; +#elif defined(GTEST_OS_WINDOWS) // We are on Windows proper. + // MSVC 8 deprecates getenv(), so we want to suppress warning 4996 + // (deprecated function) there. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + return getenv(name); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + return getenv(name); +#endif +} + +// Macro for referencing flags. +#define GTEST_FLAG(name) FLAGS_gtest_##name + +// Macros for declaring flags. +#define GTEST_DECLARE_bool(name) extern bool GTEST_FLAG(name) +#define GTEST_DECLARE_int32(name) \ + extern ::testing::internal::Int32 GTEST_FLAG(name) +#define GTEST_DECLARE_string(name) \ + extern ::testing::internal::String GTEST_FLAG(name) + +// Macros for defining flags. +#define GTEST_DEFINE_bool(name, default_val, doc) \ + bool GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_int32(name, default_val, doc) \ + ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_string(name, default_val, doc) \ + ::testing::internal::String GTEST_FLAG(name) = (default_val) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/src/gtest/internal/gtest-string.h b/src/gtest/internal/gtest-string.h new file mode 100644 index 00000000..3d20c0fc --- /dev/null +++ b/src/gtest/internal/gtest-string.h @@ -0,0 +1,280 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by testing/base/internal/gtest-internal.h. +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#include + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-port.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +namespace testing { +namespace internal { + +// String - a UTF-8 string class. +// +// We cannot use std::string as Microsoft's STL implementation in +// Visual C++ 7.1 has problems when exception is disabled. There is a +// hack to work around this, but we've seen cases where the hack fails +// to work. +// +// Also, String is different from std::string in that it can represent +// both NULL and the empty string, while std::string cannot represent +// NULL. +// +// NULL and the empty string are considered different. NULL is less +// than anything (including the empty string) except itself. +// +// This class only provides minimum functionality necessary for +// implementing Google Test. We do not intend to implement a full-fledged +// string class here. +// +// Since the purpose of this class is to provide a substitute for +// std::string on platforms where it cannot be used, we define a copy +// constructor and assignment operators such that we don't need +// conditional compilation in a lot of places. +// +// In order to make the representation efficient, the d'tor of String +// is not virtual. Therefore DO NOT INHERIT FROM String. +class String { + public: + // Static utility methods + + // Returns the input if it's not NULL, otherwise returns "(null)". + // This function serves two purposes: + // + // 1. ShowCString(NULL) has type 'const char *', instead of the + // type of NULL (which is int). + // + // 2. In MSVC, streaming a null char pointer to StrStream generates + // an access violation, so we need to convert NULL to "(null)" + // before streaming it. + static inline const char* ShowCString(const char* c_str) { + return c_str ? c_str : "(null)"; + } + + // Returns the input enclosed in double quotes if it's not NULL; + // otherwise returns "(null)". For example, "\"Hello\"" is returned + // for input "Hello". + // + // This is useful for printing a C string in the syntax of a literal. + // + // Known issue: escape sequences are not handled yet. + static String ShowCStringQuoted(const char* c_str); + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static String ShowWideCString(const wchar_t* wide_c_str); + + // Similar to ShowWideCString(), except that this function encloses + // the converted string in double quotes. + static String ShowWideCStringQuoted(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Formats a list of arguments to a String, using the same format + // spec string as for printf. + // + // We do not use the StringPrintf class as it is not universally + // available. + // + // The result is limited to 4096 characters (including the tailing + // 0). If 4096 characters are not enough to format the input, + // "" is returned. + static String Format(const char* format, ...); + + // C'tors + + // The default c'tor constructs a NULL string. + String() : c_str_(NULL) {} + + // Constructs a String by cloning a 0-terminated C string. + String(const char* c_str) : c_str_(NULL) { // NOLINT + *this = c_str; + } + + // Constructs a String by copying a given number of chars from a + // buffer. E.g. String("hello", 3) will create the string "hel". + String(const char* buffer, size_t len); + + // The copy c'tor creates a new copy of the string. The two + // String objects do not share content. + String(const String& str) : c_str_(NULL) { + *this = str; + } + + // D'tor. String is intended to be a final class, so the d'tor + // doesn't need to be virtual. + ~String() { delete[] c_str_; } + + // Returns true iff this is an empty string (i.e. ""). + bool empty() const { + return (c_str_ != NULL) && (*c_str_ == '\0'); + } + + // Compares this with another String. + // Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 + // if this is greater than rhs. + int Compare(const String& rhs) const; + + // Returns true iff this String equals the given C string. A NULL + // string and a non-NULL string are considered not equal. + bool operator==(const char* c_str) const { + return CStringEquals(c_str_, c_str); + } + + // Returns true iff this String doesn't equal the given C string. A NULL + // string and a non-NULL string are considered not equal. + bool operator!=(const char* c_str) const { + return !CStringEquals(c_str_, c_str); + } + + // Returns true iff this String ends with the given suffix. *Any* + // String is considered to end with a NULL or empty suffix. + bool EndsWith(const char* suffix) const; + + // Returns true iff this String ends with the given suffix, not considering + // case. Any String is considered to end with a NULL or empty suffix. + bool EndsWithCaseInsensitive(const char* suffix) const; + + // Returns the length of the encapsulated string, or -1 if the + // string is NULL. + int GetLength() const { + return c_str_ ? static_cast(strlen(c_str_)) : -1; + } + + // Gets the 0-terminated C string this String object represents. + // The String object still owns the string. Therefore the caller + // should NOT delete the return value. + const char* c_str() const { return c_str_; } + + // Sets the 0-terminated C string this String object represents. + // The old string in this object is deleted, and this object will + // own a clone of the input string. This function copies only up to + // length bytes (plus a terminating null byte), or until the first + // null byte, whichever comes first. + // + // This function works even when the c_str parameter has the same + // value as that of the c_str_ field. + void Set(const char* c_str, size_t length); + + // Assigns a C string to this object. Self-assignment works. + const String& operator=(const char* c_str); + + // Assigns a String object to this object. Self-assignment works. + const String& operator=(const String &rhs) { + *this = rhs.c_str_; + return *this; + } + + private: + const char* c_str_; +}; + +// Streams a String to an ostream. +inline ::std::ostream& operator <<(::std::ostream& os, const String& str) { + // We call String::ShowCString() to convert NULL to "(null)". + // Otherwise we'll get an access violation on Windows. + return os << String::ShowCString(str.c_str()); +} + +// Gets the content of the StrStream's buffer as a String. Each '\0' +// character in the buffer is replaced with "\\0". +String StrStreamToString(StrStream* stream); + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". + +// Declared here but defined in gtest.h, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template +String StreamableToString(const T& streamable); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/vsprojects/config.h b/vsprojects/config.h new file mode 100644 index 00000000..95befa10 --- /dev/null +++ b/vsprojects/config.h @@ -0,0 +1,21 @@ +/* protobuf config.h for MSVC. On other platforms, this is generated + * automatically by autoheader / autoconf / configure. */ + +/* the location of */ +#define HASH_MAP_H + +/* the namespace of hash_map/hash_set */ +#if _MSC_VER < 1310 +#define HASH_NAMESPACE std +#else +#define HASH_NAMESPACE stdext +#endif + +/* the location of */ +#define HASH_SET_H + +/* define if the compiler has hash_map */ +#define HAVE_HASH_MAP + +/* define if the compiler has hash_set */ +#define HAVE_HASH_SET diff --git a/vsprojects/convert2008to2005.sh b/vsprojects/convert2008to2005.sh new file mode 100755 index 00000000..60eccaf3 --- /dev/null +++ b/vsprojects/convert2008to2005.sh @@ -0,0 +1,20 @@ +#! /bin/sh -e + +# This script downgrades MSVC 2008 projects to MSVC 2005 projects, allowing +# people with MSVC 2005 to open them. Otherwise, MSVC 2005 simply refuses to +# open projects created with 2008. We run this as part of our release process. +# If you obtained the code direct from version control and you want to use +# MSVC 2005, you may have to run this manually. (Hint: Use Cygwin or MSYS.) + +for file in *.sln; do + echo "downgrading $file..." + sed -i -re 's/Format Version 10.00/Format Version 9.00/g; + s/Visual Studio 2008/Visual Studio 2005/g;' $file +done + +for file in *.vcproj; do + echo "downgrading $file..." + sed -i -re 's/Version="9.00"/Version="8.00"/g;' $file +done + +# Yes, really, that's it. diff --git a/vsprojects/extract_includes.bat b/vsprojects/extract_includes.bat new file mode 100755 index 00000000..85c4d92b --- /dev/null +++ b/vsprojects/extract_includes.bat @@ -0,0 +1,36 @@ +md include +md include\google +md include\google\protobuf +md include\google\protobuf\stubs +md include\google\protobuf\io +md include\google\protobuf\compiler +md include\google\protobuf\compiler\cpp +md include\google\protobuf\compiler\java +md include\google\protobuf\compiler\python +copy ..\src\google\protobuf\stubs\common.h include\google\protobuf\stubs\common.h +copy ..\src\google\protobuf\descriptor.h include\google\protobuf\descriptor.h +copy ..\src\google\protobuf\descriptor.pb.h include\google\protobuf\descriptor.pb.h +copy ..\src\google\protobuf\descriptor_database.h include\google\protobuf\descriptor_database.h +copy ..\src\google\protobuf\dynamic_message.h include\google\protobuf\dynamic_message.h +copy ..\src\google\protobuf\extension_set.h include\google\protobuf\extension_set.h +copy ..\src\google\protobuf\generated_message_reflection.h include\google\protobuf\generated_message_reflection.h +copy ..\src\google\protobuf\message.h include\google\protobuf\message.h +copy ..\src\google\protobuf\reflection_ops.h include\google\protobuf\reflection_ops.h +copy ..\src\google\protobuf\repeated_field.h include\google\protobuf\repeated_field.h +copy ..\src\google\protobuf\service.h include\google\protobuf\service.h +copy ..\src\google\protobuf\text_format.h include\google\protobuf\text_format.h +copy ..\src\google\protobuf\unknown_field_set.h include\google\protobuf\unknown_field_set.h +copy ..\src\google\protobuf\wire_format.h include\google\protobuf\wire_format.h +copy ..\src\google\protobuf\wire_format_inl.h include\google\protobuf\wire_format_inl.h +copy ..\src\google\protobuf\io\coded_stream.h include\google\protobuf\io\coded_stream.h +copy ..\src\google\protobuf\io\printer.h include\google\protobuf\io\printer.h +copy ..\src\google\protobuf\io\tokenizer.h include\google\protobuf\io\tokenizer.h +copy ..\src\google\protobuf\io\zero_copy_stream.h include\google\protobuf\io\zero_copy_stream.h +copy ..\src\google\protobuf\io\zero_copy_stream_impl.h include\google\protobuf\io\zero_copy_stream_impl.h +copy ..\src\google\protobuf\compiler\code_generator.h include\google\protobuf\compiler\code_generator.h +copy ..\src\google\protobuf\compiler\command_line_interface.h include\google\protobuf\compiler\command_line_interface.h +copy ..\src\google\protobuf\compiler\importer.h include\google\protobuf\compiler\importer.h +copy ..\src\google\protobuf\compiler\parser.h include\google\protobuf\compiler\parser.h +copy ..\src\google\protobuf\compiler\cpp\cpp_generator.h include\google\protobuf\compiler\cpp\cpp_generator.h +copy ..\src\google\protobuf\compiler\java\java_generator.h include\google\protobuf\compiler\java\java_generator.h +copy ..\src\google\protobuf\compiler\python\python_generator.h include\google\protobuf\compiler\python\python_generator.h diff --git a/vsprojects/libprotobuf.vcproj b/vsprojects/libprotobuf.vcproj new file mode 100644 index 00000000..a6808968 --- /dev/null +++ b/vsprojects/libprotobuf.vcproj @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsprojects/libprotoc.vcproj b/vsprojects/libprotoc.vcproj new file mode 100644 index 00000000..b1a6b984 --- /dev/null +++ b/vsprojects/libprotoc.vcproj @@ -0,0 +1,396 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsprojects/protobuf.sln b/vsprojects/protobuf.sln new file mode 100644 index 00000000..9523a864 --- /dev/null +++ b/vsprojects/protobuf.sln @@ -0,0 +1,50 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libprotobuf", "libprotobuf.vcproj", "{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libprotoc", "libprotoc.vcproj", "{B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}" + ProjectSection(ProjectDependencies) = postProject + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} = {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "protoc", "protoc.vcproj", "{1738D5F6-ED1E-47E0-B2F0-456864B93C1E}" + ProjectSection(ProjectDependencies) = postProject + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} = {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} = {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcproj", "{4DF72760-C055-40A5-A77E-30A17E2AC2DB}" + ProjectSection(ProjectDependencies) = postProject + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} = {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} = {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E} = {1738D5F6-ED1E-47E0-B2F0-456864B93C1E} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Debug|Win32.ActiveCfg = Debug|Win32 + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Debug|Win32.Build.0 = Debug|Win32 + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Release|Win32.ActiveCfg = Release|Win32 + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Release|Win32.Build.0 = Release|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Debug|Win32.ActiveCfg = Debug|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Debug|Win32.Build.0 = Debug|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Release|Win32.ActiveCfg = Release|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Release|Win32.Build.0 = Release|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Debug|Win32.ActiveCfg = Debug|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Debug|Win32.Build.0 = Debug|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Release|Win32.ActiveCfg = Release|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Release|Win32.Build.0 = Release|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Debug|Win32.ActiveCfg = Debug|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Debug|Win32.Build.0 = Debug|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Release|Win32.ActiveCfg = Release|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vsprojects/protoc.vcproj b/vsprojects/protoc.vcproj new file mode 100644 index 00000000..17e8474e --- /dev/null +++ b/vsprojects/protoc.vcproj @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsprojects/readme.txt b/vsprojects/readme.txt new file mode 100644 index 00000000..7f7278e0 --- /dev/null +++ b/vsprojects/readme.txt @@ -0,0 +1,71 @@ +This directory contains project files for compiling Protocol Buffers using +MSVC. This is not the recommended way to do Protocol Buffer development -- +we prefer to develop under a Unix-like environment -- but it may be more +accessible to those who primarily work with MSVC. + +Compiling and Installing +======================== + +1) Open protobuf.sln in Microsoft Visual Studio. +2) Choose "Debug" or "Release" configuration as desired. +3) From the Build menu, choose "Build Solution". Wait for compiling to finish. +4) From a command shell, run tests.exe and check that all tests pass. +5) Run extract_includes.bat to copy all the public headers into a separate + "include" directory (under the top-level package directory). +6) Copy the contents of the include directory to wherever you want to put + headers. +7) Copy protoc.exe and the two DLLs (libprotobuf and libprotoc) wherever you + put build tools. +8) Copy libprotobuf.{lib,dll} and libprotoc.{lib,dll} wherever you put + libraries. + +DLLs and Distribution +===================== + +When distributing your software to end users, we strongly recommend that you +do NOT install libprotobuf.dll or libprotoc.dll to any shared location. +Instead, keep these libraries next to your binaries, in your application's +own install directory. C++ makes it very difficult to maintain binary +compatibility between releases, so it is likely that future versions of these +libraries will *not* be usable as drop-in replacements. The only reason we +provide these libraries as DLLs rather than static libs is so that a program +which is itself split into multiple DLLs can safely pass protocol buffer +objects between them. + +If your project is itself a DLL intended for use by third-party software, we +recommend that you do NOT expose protocol buffer objects in your library's +public interface, and that you statically link protocol buffers into your +library. + +TODO(kenton): This sounds kind of scary. Maybe we should only provide static + libraries? + +Notes on Compiler Warnings +========================== + +The following warnings have been disabled while building the protobuf libraries +and compiler. You may have to disable some of them in your own project as +well, or live with them. + +C4018 - 'expression' : signed/unsigned mismatch +C4146 - unary minus operator applied to unsigned type, result still unsigned +C4244 - Conversion from 'type1' to 'type2', possible loss of data. +C4251 - 'identifier' : class 'type' needs to have dll-interface to be used by + clients of class 'type2' +C4267 - Conversion from 'size_t' to 'type', possible loss of data. +C4305 - 'identifier' : truncation from 'type1' to 'type2' +C4355 - 'this' : used in base member initializer list +C4800 - 'type' : forcing value to bool 'true' or 'false' (performance warning) +C4996 - 'function': was declared deprecated + +C4251 is of particular note. The protocol buffer library uses templates in +its public interfaces. MSVC does not provide any reasonable way to export +template classes from a DLL. However, in practice, it appears that exporting +templates is not necessary anyway. Since the complete definition of any +template is available in the header files, anyone importing the DLL will just +end up compiling instances of the templates into their own binary. The +Protocol Buffer implementation does not rely on static template members being +unique, so there should be no problem with this, but MSVC prints warning +nevertheless. So, we disable it. Unfortunately, this warning will also be +produced when compiling code which merely uses protocol buffers, meaning you +may have to disable it in your code too. diff --git a/vsprojects/tests.vcproj b/vsprojects/tests.vcproj new file mode 100644 index 00000000..9530557b --- /dev/null +++ b/vsprojects/tests.vcproj @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3